Spring MVC REST not null constraint does not work on missing request param - rest

Not null validation does not work for a case:
#RestController
public class BookController {
#GetMapping("/api/books/search")
public Page<Book> get(#RequestParam(name = "bookId") #Valid #NotNull Long bookId, Pageable pageable) {
.... // #1
}
}
If I call GET http://localhost:8080/api/books/search?bookId=
bookId is null on row #1. It's strange behaviour, cause if I do not provide bookId it fails.
#NotNull validation is not triggered.

#NotNull validation is not triggered.
When you want the validation to be triggered, the controller class must be annotated with #Validated:
#Validated
#RestController
public class BookController {
...
}
Quoting the documentation:
To be eligible for Spring-driven method validation, all target classes need to be annotated with Spring’s #Validated annotation.
The #RequestParam annotation has a required element where the default value is true. It simply indicates that the parameter itself must be present in the URL (either ?id or ?id=), but it doesn't require a non-null value for that parameter. If you need to validate the value of such parameter, then you should use Bean Validation annotations.
Consider, for instance, the following controller method:
#GetMapping("/foo")
public ResponseEntity<Foo> get(#RequestParam("id") Long id) {
...
}
The following request will result in a response with the 400 status code because the id parameter is not present:
GET /foo HTTP/1.1
Host: example.org
On the other hand, the following request is considered to be valid because the id parameter is present (even though there's no value associated with it):
GET /foo?id HTTP/1.1
Host: example.org
To refuse null values, use the #NotNull annotation (and ensure that controller class is annotated with #Validated):
#GetMapping("/foo")
public ResponseEntity<Foo> get(#RequestParam("id") #NotNull Long id) {
...
}

There are different concepts. Method parameters annotated with #RequestParam are by default required. You can set #RequestParam(required = false) to disable this.
Your parameter however is of type Long (object). You can not give it and then by default it will be null (since you did not set a default - like #RequestParam(defaultValue = )).
Best way is to either set a default value or to check for null in your method.

Related

Quarkus: How to make request #HeaderParam key as Mandatory Key

How to make the employeeId and employeeName both HeaderParam keys as Mandatory in Quarkus
#POST
#Path("/employee")
#Produces(MediaType.APPLICATION_JSON)
public Response SaveEmployeeDetails(#RequestBody Employee employeeObj, #HeaderParam("empId") BigInteger eemployeeId, #HeaderParam("empName") String employeeName) throws Exception {
//Service Layer Code
}
As the JAX-RS specification does not provide a way to do this, your best best is to the quarkus-hibernate-validator extension and add the #javax.validation.constraints.NotNull annotation to each parameter that needs to not be null.

ReSTful service getting contradict due to path parameter value has forward slash

I have API like this-
/objectname/name
/objectname/collection/id
Both API's are indirectly related.
Problem occurs when calling first API with name value as "A/B Type". So rest controller actually calling second API rather first (/objectname/A/B Type) because forward slash. How to deal with this situation.
As a side note I am encoding the parameters values.
I developed the restful services using SpringBoot and RestTemplate.
The conflict comes by specifying the name directly in the resource path and passed to the function as a #PathVariable.
Your code looks something like this:
#RequestMapping(value = "objectname/{name}", method = RequestMethod.GET)
public String yourMethodName(#PathVariable String name){
return name;
}
What I would recommend in order to avoid this kind of conflict is (if you're allowed to modify the #RestController or #RepositoryRestResource layers) to pass the value of the object in a #RequestParam
For instance:
#RequestMapping(value = "/objectname", method = RequestMethod.GET)
public String yourMethodName(#RequestParam(name = "name", required = true) String name){
return name;
}
That said, When you are constructing your the request using RestTemplate then you should url encode your name (A%2FB%20Testing) and construct the following url:
http://localhost:8080/objectname?name=A%2FB%20Testing
I tested this locally and worked alright for me.

Spring MVC form:checkbox not working

I am having an issue with the Spring form:checkbox tag.
I currently have a JSP page with a form:checkbox tag bound to a Java boolean field. When I put a tick in the checkbox and submit the form the value is false.
Here is the checkbox on my JSP:
<form:checkbox id="field_termsandconditions" path="agreeTermsAndConditions" />
My GET controller:
#RequestMapping(value = "/page1.htm", method = RequestMethod.GET)
public String getPage(HttpServletRequest request, ModelMap model) {
model.addAttribute("MyObject", new MyObject());
return getURL(request);
}
My POST controller:
#RequestMapping(value = "/page1.htm", method = RequestMethod.POST)
public String processPage(HttpServletRequest request,
HttpServletResponse response,
ModelMap model,
MyObject myObject,
BindingResult bindingResult) {
System.out.println(myObject.isAgreeTermsAndConditions);
}
myObject.isAgreeTermsAndConditions is always false when it hits the POST controllers even when checked!
Any ideas?
This might be a little late to answer, but maybe it will help some other person.
When you auto-generate getter and setters for boolean values it is very often generated without 'is' prefix.
For instance, in the case mentioned above the generated setter for 'isAgreeTermsAndConditions' property might be the following: 'setAgreeTermsAndConditions()', note there is no 'is' prefix in the method. The same true for the getters as well.
Since property getter and setters names are used find and bind to the model property, the checkbox might be not shown on the UI or not working properly if there are property name and getters/setters mismatch.
Make sure the property 'isAgreeTermsAndConditions' has the following getters/setters method names: getIsAgreeTermsAndConditions()/setIsAgreeTermsAndConditions(...)

RESTful webservices: query parameters

I am using JAX-RS with RESTEasy.
I want to know if we can have represent different resources with path differentiated only by order and number of query parameters?
e.g.
/customer/1234
/customer?id=1234
/customer?name=James
Can I create three different methods e.g.
#Path("customer/{id}")
public Response get(#PathParam("id") final Long id) {
..
}
#Path("customer?id={id}")
public Response get(#QueryParam("id") final Long id) {
..
}
#Path("customer?name={name}")
public Response get(#QueryParam("name") final String name) {
..
}
Will this work, can I invoke different methods by differentiating path like this?
Thanks
This is a valid #Path:
#Path("customer/{id}") // (1)
These are not:
#Path("customer?id={id}") // (2)
#Path("customer?name={name}") // (3)
They are the same because the boil down to
#Path("customer")
which you could use.
So you can have (1) and one of (2) and (3). But you can't have (2) and (3) at the same time.
#QueryParam parameters are not part of the #Path. You can access them as you do in the signature of the method but you can't base the routing of JAX-RS on them.
Edit:
You can write one method that accepts both id and name as #QueryParam. These query parameters are optional.
#Path("customer")
public Response get(#QueryParam("id") final String id,
#QueryParam("name") final String name) {
// Look up the Customers based on 'id' and/or 'name'
}

Nested Jsr 303 validation annotations

I would like to use a class level annotation constraint. However I cannot get the inner constraints to validate automatically. I'd like to help with one part, to incorporate validation groups into this technique.
#ConstraintA({
#ConstraintB(stuff),
#ConstraintB(stuff, groups=SomeGroup.class)
})
public class Form{
}
I currentily trigger the constraints like so.
if(constraint instanceof ConstraintB){
new ConstraintBValidator().isValid(target, context);
}
However this sucks obviously.I will eventually refactor to trigger the isValid methods through a call to the AnnotationInvocationHandler.invoke() method, but im a little way from that still.
My issue is that all ConstraintB instances are passed into my ConstraintA. I wish only the ones with the appropriate groups to be passed to ConstraintA. I doubt this ability exists, so how can i identify which groups need to be triggered and which dont?
I dont see in my debug, any objects which specify which groups should be triggered?
Any ideas?
I've found a pattern in the JSR 303 spec that allows this type of validation. Its a recursive pattern that wont execute the parent validation rule, only fulfill the nested validations. This is VERY handy. My nested validation rules are conditionally based on other properties values so it allows conditional validation with nested jsr303 annotations.
#Documented
#Constraint(validatedBy = ZipCodeValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface ZipCode {
String countryCode();
String message() default "{com.acme.constraint.ZipCode.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Defines several #ZipCode annotations on the same element
* #see (#link ZipCode}
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#interface List {
ZipCode[] value();
}
my own validation is more like this:
#RequiredIf ({
#RequiredIf(ifField="field1", matches={"true","somethingElse"}, requiredField="anotherField", andDisplay="error.code.msg"),
#RequiredIf(ifField="field2", matches={"true","somethingElse"}, requiredField="anotherField", andDisplay="error.code.msg")
})