JAX-RS validation messages when in interface - java

I have a dropwizard 1.0.6 application. I want to put all JAX-RS and validation annotations on an interface and then register my resource to implement this interface, similar to:
#Path("/user")
#Produces(MediaType.APPLICATION_JSON)
public interface UserEndpoint {
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
Response createUser(
#FormParam("username") #NotEmpty #Email String username,
#FormParam("password") #Size(min=4) String password);
}
and the resepective resource:
public class UserResource implements UserEndpoint {
public Response createUser(String username, String password) {
//my logic here
}
}
This works amazingly, except for the validation messages in the error responses, which have become:
createUser.arg0 not a well-formed email address
while I would expect them to be:
form field username not a well-formed email address
UPDATE:
If I add the JAX-RS and validation annotations straight to UserResource, then the validation message is what I would expect: it reports the name of the parameter as specified in the #FormParam annotation. This is what I want to achieve, but having the annotations in the interface.

Annotations are not inherited, unless they are annotated with #Inherited.
Per the Java Language Specification
Annotation inheritance only works on classes (not methods, interfaces, or constructors)
So it is up to the code that wants to use the annotations if it will check the parent class/interface for annotations.
In other words you will need to contribute to Dropwizard to make it search annotations from parent classes/interfaces

the real name of the parameters is not available for the JVM at runtime so that's why it is replaced by arg0 , in order to fix this issue you'll had to deal with ParameterNameProvider which will use reflection in order to return a list of String from getParameterNames(Method yourExceptionImpl)

Related

Spring boot #RequestBody default POJO mapping behavior?

I have a java class with uppercase field names and some of them with under scroll, like this:
public class DATADto {
private String UPPERCASE;
private String UNDER_SCROLL;
public String getUPPERCASE() { return UPPERCASE; }
public void setUPPERCASE(String s) { UPPERCASE = s; }
...//setters and getters
}
and I used this in a rest endpoint that accepts json in a spring rest controller:
#RestController
#RequestMapping({"/api/path"})
public class MyRestController {
#PostMapping(path = {"/Data"}, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> useDATADto(#RequestBody DATADto aDATADto ) {
//do something
}
}
what JSON fields do I need to send by default and why?
The story goes like this..
Spring Boot by default uses Jackson ObjectMapper to serialize and deserialize Java objects.
In this context, by serialization we mean the conversion of java objects into json, deserialization is the reverse process.
Regarding the #RequestBody annotation, the following is written in the documentation:
Annotation indicating a method parameter should be bound to the body
of the web request. The body of the request is passed through an
HttpMessageConverter to resolve the method argument depending on the
content type of the request. Optionally, automatic validation can be
applied by annotating the argument with #Valid.
In short, #RequestBody annotation tells Spring to deserialize an incoming request body into an object passed as a parameter to the handler method. Spring achieves this using MessageConverter
Since Spring Boot uses Jackson by default for serializing and deserializing request and response objects in your REST APIs, and Jackson uses MappingJackson2HttpMessageConverter, so that will be message converter implementation that spring will use. You can read more about that here.
The important thing is that Jackson uses Java Bean naming conventions to figure out the json properties in a Java class. Acutally it uses default PropertyNamingStrategy . Here is what is written in documentation:
In absence of a registered custom strategy, default Java property
naming strategy is used, which leaves field names as is, and removes
set/get/is prefix from methods (as well as lower-cases initial
sequence of capitalized characters).
So, since you didn't set any naming strategy, it will use default one.
Beacause of that, if you send payload like this :
{
"uppercase": "YOUR_VALUE",
"under_scroll": "YOUR_VALUE"
}
That won't work, you will get exception, since there jackson won't find under_scroll property in your class, it will look for under_SCROLL , therefore this payload:
{
"uppercase": "YOUR_VALUE",
"under_SCROLL": "YOUR_VALUE"
}
will work.
To change default PropertyNamingStrategy check
this article.
It will depend on the Jackson property naming strategy. The default is LOWER_CAMEL_CASE , so your request body should look like this:
{
"uppercase": "test",
"under_scroll": "test"
}
For all possible configurations of the naming strategy for Jackson please refer to the document «Class PropertyNamingStrategy»
If you're using Spring, you may use this property to configure the naming strategy:
spring.jackson.property-naming-strategy
Another possible way will be the bean configuration:
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder jacksonMapper = new Jackson2ObjectMapperBuilder();
jacksonMapper.propertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE);
return jacksonMapper;
}
Additional note:
Your current naming approach doesn't follow the Java Code Conventions. If you need to process JSON with some specific naming format better to use the #JsonProperty annotation on the fields of your POJO.
Please see the example below:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
#Data
public class DATADto {
#JsonProperty("UPPERCASE")
private String uppercase;
#JsonProperty("UNDER_SCROLL")
private String underScroll;
}
You should send post request to /api/path/data with this request body:
{
"uppercase": "YOUR_VALUE",
"under_scroll": "YOUR_VALUE"
}

#QueryParam or HttpServletRequest in java

I have resource class - UserResource interface and I have defined an endpoint as getUsers.
Now I want to filter these users based on users status - (Active, Inactive) and its not mandatory so if I does not pass the status it will fetch all the users.
Now the question is should I pass this as #QueryParam or get it from
HttpServletRequest - httpServletRequest.getParameter("Status").
Which one from the above two is best way and in what scenario I should use which one.
1. First way is pass the status as query param and define in the resource file itself. Here UserResource is the controller interface or resource class. In getUsers method has #QueryParam.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource{
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers(#QueryParam("status") String status);
}
#Component
Public class UsersResourceImpl implement UserResource{
public Response getPlan(String status){
String userStatus = status;
// some logic
}
}
2. Second way is get the query param from HttpServletRequest. so I have
autowired the HttpServletRequest and getting the query param from the
httpservletrequest.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource {
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers();
}
import javax.servlet.http.HttpServletRequest;
#Component
Public class UsersResourceImpl implements UserResource{
#Autowired
private HttpServletRequest httpRequest;
public Response getPlan(String status){
String status = httpRequest.getParameter(status)
// some logic
}
}
'''
Well, I honestly don't see any appealing reason to avoid using the #QueryParam annotation given that you need the value from a query parameter.
Some benefits of using #QueryParam that I can think of:
The #QueryParam annotation will automatically bind the value(s) of a query parameter to a resource method parameter, resource class field, or resource class bean property. So you won't need to extract and parse parameters manually, once you respect some rules described in the documentation:
The type T of the annotated parameter, field or property must either:
Be a primitive type
Have a constructor that accepts a single String argument
Have a static method named valueOf or fromString that accepts a single
String argument (see, for example, Integer.valueOf(String))
Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.
#QueryParam can be combined with #DefaultValue to define a default value for the parameter in case it's not present in the request.
If multiple endpoints support the same query parameters, you could aggregate them in a class and receive an instance of such class as a #BeanParameter.
Go with annotations (i.e #QueryParam) that's why we chose such framework , remember convention over configuration.

Spring Security + MVC : same #RequestMapping, different #Secured

Let say we have an API endpoint configured using Spring MVC and Spring Security. We would like to be able to handle pairs of #RequestMapping and #Secured annotations where the only #Secured annotation values differ from pair to pair. This way, we would be able to return a different response body depending on security rules for the same request.
This may allow our code to be more maintainable by avoiding to check for security rules directly into the method body.
With a not working example, here is what we would like to do :
#Controller
#RequestMapping("/api")
public class Controller {
#Secured ({"ROLE_A"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid) {
// Returns something for users having ROLE_A
}
#Secured ({"ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomethingDifferent(#PathVariable("uid") String uid) {
// Returns something different for users having ROLE_B
}
}
How can we achieve this ?
And if this can be done: How the priority should be managed for a user who has both ROLE_A and ROLE_B ?
Assuming you are using Spring 3.1 (or up) together with the RequestMappingHandlerMapping (and RequestMappingHandlerAdapter) you can extend the request mapping mechanism. You can do this by creating your own implementation of the RequestCondition interface and extend the RequestMappingHandlerMapping to construct this based on the #Secured annotation on your method.
You would need to override the 'getCustomMethodCondition' method on the RequestMappingHandlerMapping and based on the Method and the existence of the #Secured annotation construct your custom implementation of the RequestCondition. All that information is then taken into account when matching incoming requests to methods.
Related answers (although not specific for #Secured annotations but the mechanism is the same) is also to be found here or here
I don't think you can do this in spring-mvc, since both routes have exactly the same #RequestMapping (#Secured) is not taken into account by the route engine of spring-mvc. The easiest solution would be to do this:
#Secured ({"ROLE_A", "ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid, Principal p) {
// Principal p gets injected by spring
// and you need to cast it to check access roles.
if (/* p.hasRole("ROLE_A") */) {
return "responseForA";
} else if (/* p.hasRole("ROLE_B") */) {
return "responseForB";
} else {
// This is not really needed since #Secured guarantees that you don't get other role.
return 403;
}
}
However, I would change your design, since the response is different per role, why not have 2 separate request mappings with slightly different URLs? If at some point you have users with role A and B at the same time, you can't let the user choose what response to get (think, for example, of the public and private profiles of LinkedIn)

Spring MVC URL for the AbstractController

I have a controller that inherits from the org.springframework.web.servlet.mvc.AbstractController class
I have configurated it in this way:
<bean name="/gameServiceController.json" class="xx.xxx.GameController"/>
so can accepts url of this form
http://<hostname>:<port>/<context-path>/gameServiceController.json
but the customer has provided to me the requirement to write URL in this way
http://<hostname>:<port>/<context-path>/createNewGame?parameter=<value>
but I think that is not possible to map this type of URL with my controller. Anyone know the type of configuration that can be used in order to configure this type of URL mapping ?
Otherwise, is legal to ask to change the format of the URL in this way
http://<hostname>:<port>/<context-path>/gameServiceController.json?command=createNewGame&phoneNumber=<phoneNumber>
so I can manage the command parameter in the "handleRequestInternal" method of my custom controller that inherits from the org.springframework.web.servlet.mvc.AbstractController class ??
Don't use the legacy Controller framework, use annotated controllers. There, you can easily use URL templates, something like this:
#Controller
public class GameController{
#RequestMapping(value="/createNewGame?parameter={param}",
method=RequestMethod.GET)
public String createNewGame(#PathVariable String param, Model model) {
// do stuff here
return "viewName";
}
}

What does the #Valid annotation indicate in Spring?

In the following example, the ScriptFile parameter is marked with an #Valid annotation.
What does #Valid annotation do?
#RequestMapping(value = "/scriptfile", method = RequestMethod.POST)
public String create(#Valid ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {
if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");
if (result.hasErrors()) {
modelMap.addAttribute("scriptFile", scriptFile);
modelMap.addAttribute("showcases", ShowCase.findAllShowCases());
return "scriptfile/create";
}
scriptFile.persist();
return "redirect:/scriptfile/" + scriptFile.getId();
}
It's for validation purposes.
Validation It is common to validate a
model after binding user input to it.
Spring 3 provides support for
declarative validation with JSR-303.
This support is enabled automatically
if a JSR-303 provider, such as
Hibernate Validator, is present on
your classpath. When enabled, you can
trigger validation simply by
annotating a Controller method
parameter with the #Valid annotation:
After binding incoming POST
parameters, the AppointmentForm will
be validated; in this case, to verify
the date field value is not null and
occurs in the future.
Look here for more info:
http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/
Adding to above answers, take a look at following. AppointmentForm's date column is annotated with couple of annotations. By having #Valid annotation that triggers validations on the AppointmentForm (in this case #NotNull and #Future). These annotations could come from different JSR-303 providers (e.g, Hibernate, Spring..etc).
#RequestMapping(value = "/appointments", method = RequestMethod.POST)
public String add(#Valid AppointmentForm form, BindingResult result) {
....
}
static class AppointmentForm {
#NotNull #Future
private Date date;
}
#Valid in itself has nothing to do with Spring. It's part of Bean Validation specification(there are several of them, the latest one being JSR 380 as of second half of 2017), but #Valid is very old and derives all the way from JSR 303.
As we all know, Spring is very good at providing integration with all different JSRs and java libraries in general(think of JPA, JTA, Caching, etc.) and of course those guys took care of validation as well. One of the key components that facilitates this is MethodValidationPostProcessor.
Trying to answer your question - #Valid is very handy for so called validation cascading when you want to validate a complex graph and not just a top-level elements of an object. Every time you want to go deeper, you have to use #Valid. That's what JSR dictates. Spring will comply with that with some minor deviations(for example I tried putting #Validated instead of #Valid on RestController method and validation works, but the same will not apply for a regular "service" beans).
I wanted to add more details about how the #Valid works, especially in spring.
Everything you'd want to know about validation in spring is explained clearly and in detail in https://reflectoring.io/bean-validation-with-spring-boot/, but I'll copy the answer to how #Valid works incase the link goes down.
The #Valid annotation can be added to variables in a rest controller method to validate them. There are 3 types of variables that can be validated:
the request body,
variables within the path (e.g. id in /foos/{id}) and,
query parameters.
So now... how does spring "validate"? You can define constraints to the fields of a class by annotating them with certain annotations. Then, you pass an object of that class into a Validator which checks if the constraints are satisfied.
For example, suppose I had controller method like this:
#RestController
class ValidateRequestBodyController {
#PostMapping("/validateBody")
ResponseEntity<String> validateBody(#Valid #RequestBody Input input) {
return ResponseEntity.ok("valid");
}
}
So this is a POST request which takes in a request body, and we're mapping that request body to a class Input.
Here's the class Input:
class Input {
#Min(1)
#Max(10)
private int numberBetweenOneAndTen;
#Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
private String ipAddress;
// ...
}
The #Valid annotation will tell spring to go and validate the data passed into the controller by checking to see that the integer numberBetweenOneAndTen is between 1 and 10 inclusive because of those min and max annotations. It'll also check to make sure the ip address passed in matches the regular expression in the annotation.
side note: the regular expression isn't perfect.. you could pass in 3 digit numbers that are greater than 255 and it would still match the regular expression.
Here's an example of validating a query variable and path variable:
#RestController
#Validated
class ValidateParametersController {
#GetMapping("/validatePathVariable/{id}")
ResponseEntity<String> validatePathVariable(
#PathVariable("id") #Min(5) int id) {
return ResponseEntity.ok("valid");
}
#GetMapping("/validateRequestParameter")
ResponseEntity<String> validateRequestParameter(
#RequestParam("param") #Min(5) int param) {
return ResponseEntity.ok("valid");
}
}
In this case, since the query variable and path variable are just integers instead of just complex classes, we put the constraint annotation #Min(5) right on the parameter instead of using #Valid.
IIRC #Valid isn't a Spring annotation but a JSR-303 annotation (which is the Bean Validation standard). What it does is it basically checks if the data that you send to the method is valid or not (it will validate the scriptFile for you).
public String create(#Valid #NotNull ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {
if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");
I guess this #NotNull annotation is valid therefore if condition is not needed.
I think I know where your question is headed. And since this question is the one that pop ups in google's search main results, I can give a plain answer on what the #Valid annotation does.
I'll present 3 scenarios on how I've used #Valid
Model:
public class Employee{
private String name;
#NotNull(message="cannot be null")
#Size(min=1, message="cannot be blank")
private String lastName;
//Getters and Setters for both fields.
//...
}
JSP:
...
<form:form action="processForm" modelAttribute="employee">
<form:input type="text" path="name"/>
<br>
<form:input type="text" path="lastName"/>
<form:errors path="lastName"/>
<input type="submit" value="Submit"/>
</form:form>
...
Controller for scenario 1:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee){
return "employee-confirmation-page";
}
In this scenario, after submitting your form with an empty lastName field, you'll get an error page since you're applying validation rules but you're not handling it whatsoever.
Example of said error:
Exception page
Controller for scenario 2:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee,
BindingResult bindingResult){
return bindingResult.hasErrors() ? "employee-form" : "employee-confirmation-page";
}
In this scenario, you're passing all the results from that validation to the bindingResult, so it's up to you to decide what to do with the validation results of that form.
Controller for scenario 3:
#RequestMapping("processForm")
public String processFormData(#Valid #ModelAttribute("employee") Employee employee){
return "employee-confirmation-page";
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidFormProcessor(MethodArgumentNotValidException ex){
//Your mapping of the errors...etc
}
In this scenario you're still not handling the errors like in the first scenario, but you pass that to another method that will take care of the exception that #Valid triggers when processing the form model. Check this see what to do with the mapping and all that.
To sum up: #Valid on its own with do nothing more that trigger the validation of validation JSR 303 annotated fields (#NotNull, #Email, #Size, etc...), you still need to specify a strategy of what to do with the results of said validation.
Hope I was able to clear something for people that might stumble with this.
Just adding to the above answer, In a web application
#valid is used where the bean to be validated is also annotated with validation annotations e.g. #NotNull, #Email(hibernate annotation) so when while getting input from user the values can be validated and binding result will have the validation results.
bindingResult.hasErrors() will tell if any validation failed.
Another handy aspect of #Valid not mentioned above is that (ie: using Postman to test an endpoint) #Valid will format the output of an incorrect REST call into formatted JSON instead of a blob of barely readable text. This is very useful if you are creating a commercially consumable API for your users.

Categories

Resources