How to validate the range of a query parameter on Spring Boot - spring-boot

There's a payload that I need to paginate and now it works but I want to validate the range of limit query parameter. I tried adding the #Range annotation but it doesn't seem to work. Can someone help me to validate this?
#GetMapping("/ambassadors")
#ResponseStatus(value = HttpStatus.OK)
public Page<Ambassador> getAmbassadors(#Range(min = 10, max = 20) #RequestParam int limit,
#Range(min = 0) #RequestParam int pageNumber) {
Pageable pageable = PageRequest.of(pageNumber, limit);
return ambassadorRepository.findAll(pageable);
}

Try adding #Validated annotation to your controller.
ex:
#RestController
#Validated
public class TestController {
}

Related

Spring boot search filter and pagination

I want to use pagination, filter and search in a single endpoint of a RestController.
I am using spring 5. Can anyone suggest what will the best approach for this solution.
public class SearchFilter {
private String searchValue;
#Min(0)
private int offset;
#Min(1)
private int limit;
}
#RestController
#RequestMapping("/texts")
public class MyController {
#PostMapping("/search")
public ResponseEntity<List<Text>> search(#RequestBody #Valid SearchFilter filter) {
Query query = new Query(Criteria.where("text").regex(Pattern.compile(filter.getSearchValue()).pattern(), "i"))
.skip(filter.getOffset())
.limit(filter.getLimit());
...
Regarding the pagination (kind of limit and offset above), you can use also #RequestParam(required=false) for the pageSize and pageNumber, and then do if possible PageRequest.of(pageNumber, pageSize).

spring boot custom validation message not shown

I have an endpoint that accepts query parameters bound to a class GetParams:
public class GetParams{
#Min(value = 0, message = "OFFSET_INVALID")
private Integer offset;
#Min(value = 0, message = "LIMIT_INVALID")
private int limit;
public GetParams() {
this.offset = 0;
this.limit = 100;
}
public int getOffset() {
return offset;
}
public int getLimit() {
return limit;
}
public void setOffset(int offset) {
this.offset = offset;
}
public void setLimit(int limit) {
this.limit = limit;
}
}
This is the endpoint in my controller:
#GetMapping(value = "/applications")
public ResponseEntity<Data> getApplications( #Validated GetParams parameters) { ... }
When I send a GET request that violates one of the constraints, for example:
GET /applications?offset=-20&limit=100
The framework returns a 400, but without my messages, and in fact without a response body and without a stack trace printed into the console! When the query params are valid, the result is valid as well. Why is this?
Thanks.
For a GET request you can't validate the request-params binding with object like you did above. You have to validate each param by themselves separately like this:
public ResponseEntity<Data> getApplications(#RequestParam #Min(1) #Max(value=7, message="you custom message") Integer offset, #RequestParam #Min(3) Integer params2, ... )
{
//your code/logic
}
You can validate that way only for POST and other requests with #RequestBody which have body in their requests.
From root path you can set 'include binding errors' in "resources/application.properties" to "always". Same goes for 'message' as well.
server.error.include-message=always
server.error.include-binding-errors=always
Request parameters are to mapped using #RequestParam.
Try
#GetMapping(value = "/applications")
public ResponseEntity<Data> getApplications( #Validated #ReqeustParam #Min(value = 0, message = "OFFSET_INVALID") Integer offset) { ... }`

How to get a param from URL Rest Service that is after a question mark (?)

I have the following String that I receive in my rest service:
.6.75:5050/pretups/C2SReceiver?REQUEST_GATEWAY_CODE=8050122997&REQUEST_GATEWAY_TYPE=EXTGW&LOGIN=CO8050122997_EXTGW&PASSWORD=89b87741ca3f73b0b282ae165bad7501&SOURCE_TYPE=XML&SERVICE_PORT=190
I have the following code:
#Controller
public class servicioscontroller {
#RequestMapping(value = "/pretups/{p1}?{trama}", method = RequestMethod.GET)
#ResponseBody
public String enviarTrama(#PathVariable("p1") String p1,#PathVariable("trama") String trama){
return p1+trama;
}
}
And im getting this result:
C2SReceive
I need also the string after that ?, what im doing wrong or how do I get that? thanks
You should use #RequestParam:
#Controller
public class servicioscontroller {
#RequestMapping(value = "/pretups/{p1}", method = RequestMethod.GET, params = ["trama"])
#ResponseBody
public String enviarTrama(
#PathVariable("p1") String p1,
#RequestParam(name = "trama", required = "true") String trama
){
return p1+trama;
}
}
Note that you have to put request params in the #RequestMapping's params attribute instead of the path string.
The difference between #RequestParam and #PathParam is described in this answer.

Optional query param type in Spring WebMVC

Spring allows to do Optional for RequestParams. How to do this for query parameters?
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Page<Deal> getDeals(Optional<PageRange> pageRange) {
return dealService.findDeals(pageRange.orElse(DEFAULT_PAGE_RANGE));
}
The query params wrapper:
#Value
#Builder
#AllArgsConstructor
public class PageRange {
int page;
int size;
}
This fails if either page or size is not passed. Why?

Validate partial Modal using Spring #Valid annotation

I have a User Modal
public class RegisterUser {
#Size(min = 2, max = 30)
private String fname;
#Size(min = 2, max = 30)
private String lname;
#NotEmpty
#Size(min = 6, max = 15)
private String password;
....
#NotEmpty
private String publicProfile;
... getters and setters
}
1) I want to use this modal during registration action (fname, lname, password etc but without publicProfile field)
2) I want to use this modal during myprofile action (all fields except password)
My action for register:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String submitRegisterForm(
#Valid RegisterUser registerUser,
BindingResult result,
Model m) {
....
}
Here I don't intend to provide 'publicprofile' on jsp and therefore do not want to validate this field although my Modal has #NotEmpty annotation
My action for myprofile
#RequestMapping(value = "/myprofile", method = RequestMethod.POST)
public String submitMyprofileForm(
#Valid RegisterUser registerUser,
BindingResult result,
Model m) {
....
}
Here I don't intend to provide 'password' field on jsp and therefore do not want to validate this field although my Modal has #NotEmpty and #Size(min = 6, max = 15) annotation
My question is how can I achieve this ?
Is there any way where I can say in this modal for this action validate only mentioned fields?
Thanks in advance
Manisha
You can use Validation Groups (for different scenarios) and Spring's #Validated annotation to specify which group you want to use
I don't know if this is possible with Bean Validation, but you can set up different implementations of Spring's Validation Interface for different request parameters.
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String submitRegisterForm(#Valid RegisterUser registerUser, ...
and
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String submitMyprofileForm(#Valid RegisterUser registerUserProfile, ...
And then you can use #InitBinder to connect different Validators to your request params. You would add these methods to your controller. Just omit the validation you dont want in the second Validator.
#InitBinder("registerUser")
protected void initUserBinder(WebDataBinder binder) {
binder.setValidator(new RegisterUserValidator());
}
#InitBinder("registerUserProfile")
protected void initUserBinderProfile(WebDataBinder binder) {
binder.setValidator(new RegisterUserProfileValidator());
}
Then you would need to do the annotation stuff manually. You could also use inheritance for your Validators, because they are exactly the same, except the one additional field validation for registration forms.
public class RegisterUserValidator implements Validator {
public boolean supports(Class clazz) {
return RegisterUser.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "publicProfile", "empty");
RegisterUser r = (RegisterUser) obj;
if (r.getFname().length() < 2) {
e.rejectValue("fname", "min");
} else if (r.getFname().length() > 30) {
e.rejectValue("fname", "max");
}
// ...
}
}

Resources