How to validate Controller method argument in Spring with JSR validations? - java

I am validating my form field with given piece of code.
//controller method
public String addBusiness(#Valid #ModelAttribute("myForm") MyForm myForm, ...)
{
//logic will go here.
}
//form
#Component
public class MyForm{
#Pattern(regexp = "[0-9]{3,10}", message = "should be valid number")
public String getZip_code()
{
return this.zip_code;
}
}
Now I want same validation on zip_code in another method of controller like,
#RequestMapping(value = "${validation.url}", method = RequestMethod.GET)
#ResponseBody
public List<String> getCityList(#RequestParam(value = "zip_code", required = true) final String zip_code)
{
//logic goes here
}
How is it possible?

It's not. #Valid doesn't apply to #RequestParam annotated parameters. You can create a custom HandlerMethodArgumentResolver to do this or do the validation yourself in the method.

Related

How to narrow down methods with body parameters in SpringMVC?

It's pretty like params in #RequestMapping annotation, which narrows down the matching methods with query parameters. Instead, I want to achieve this with body parameters (like JSON-format). Do I need to implement a custom #RequestMapping annotation?
YOu can achieve it using #RequestBody. It matches payload with names specified in code.
https://www.baeldung.com/spring-request-response-body
You just have to use #RequestBody. Note that this not only allows POJO, you can use it with a Map too.
With a POJO:
#RequestMapping(value="/",method = RequestMethod.POST)
public String doSomething(#RequestBody MyDto dto) {
return service.requestOTP(dto.getField1(), dto.getField2());
}
With a Map:
#RequestMapping(value="/",method = RequestMethod.POST)
public String doSomething(#RequestBody Map<String, Object> body) {
return service.requestOTP(body.get("field1").toString(), body.get("field2").toString());
}
NOTE: If you would like to validate properties of an object used with #RequestBody, you can add #Valid annotation. For instance, the following will cascade the validation on the model and the models sub model fields(if found).
public class MyDto {
#NotNull
#Valid
private String field1;
private String field2;
//getters and setters
}
#RequestMapping(value="/",method = RequestMethod.POST)
public String doSomething(**#Valid** #RequestBody MyDto dto) {
return service.requestOTP(dto.getField1(), dto.getField2());
}
Check here for a brave introduction for #Valid https://www.logicbig.com/tutorials/java-ee-tutorial/bean-validation/cascaded-validation.html

Spring boot - class validation not working on REST API

I have the following REST API:
#ResponseBody
#PostMapping(path = "/configureSegment")
public ResponseEntity<ResultData> configureSegment(#RequestParam() String segment,
#RequestBody #Valid CloudSegmentConfig segmentConfig
) {
CloudSegmentConfig:
#JsonProperty(value="txConfig", required = true)
#NotNull(message="Please provide a valid txConfig")
TelemetryConfig telemetryConfig;
#JsonProperty(value="rxConfig")
ExternalSourcePpdkConfig externalSourcePpdkConfig = new ExternalSourcePpdkConfig(true);
TelemetryConfig:
public class TelemetryConfig {
static Gson gson = new Gson();
#JsonProperty(value="location", required = true)
#Valid
#NotNull(message="Please provide a valid location")
Location location;
#Valid
#JsonProperty(value="isEnabled", required = true)
#NotNull(message="Please provide a valid isEnabled")
Boolean isEnabled;
Location:
static public enum Location {
US("usa"),
EU("europe"),
CANADA("canada"),
ASIA("asia");
private String name;
private Location(String s) {
this.name = s;
}
private String getName() {
return this.name;
}
}
When I'm trying to send the following JSON:
{
"txConfig": {
"location": "asdsad"
}
}
The API return empty response 400 bad request, while I expect it to validate the location to be one of the ENUMs of the class. I also expect it to validate the isEnable parameter while it doesn't although I added all possible annotation to it..
Any idea?
Use #Valid annotation on TelemetryConfig telemetryConfig and no need to use #Valid on the field of TelemetryConfig class.
#Valid
TelemetryConfig telemetryConfig;
And for enum subset validation you can create a customer validator with annotation and use it.
A good doc about this Validating a Subset of an Enum

How can I detect if the JSON object within Request body is empty in Spring Boot?

I want to return an error when the body of a REST request is empty (e.g contains only {}) but there is no way to detect if the request body contains an empty JSON or not.
I tried to change #RequestBody(required = true) but it's not working.
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(#PathVariable("id") Integer id,
#Valid #RequestBody BookDto newBook) {
Book addedBook = bookService.updateBook(newBook);
return new ResponseEntity<>(addedBook,HttpStatus.OK);
}
If the body sent contains an empty JSON I should return an exception.
If the body is not empty and at least one element is provided I won't return an error.
Try #RequestBody(required = false)
This should cause the newBook parameter to be null when there is no request body.
The above still stands and is the answer to the original question.
To solve the newly edited question:
Change the #RequestBody BookDto newBook parameter to a String parameter
(for example, #RequestBody String newBookJson).
Perform pre-conversion validation (such as, "is the body an empty JSON string value").
If the body contains valid JSON,
parse the JSON into to an object (example below).
#Autowired
private ObjectMapper objectMapper; // A Jackson ObjectMapper.
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(
#PathVariable("id") Integer id,
#Valid #RequestBody String newBookJson)
{
if (isGoodStuff(newBookJson)) // You must write this method.
{
final BookDto newBook = ObjectMapper.readValue(newBookJson, BookDto.class);
... do stuff.
}
else // newBookJson is not good
{
.. do error handling stuff.
}
}
Let's suppose you have a Class BookDto :
public class BookDto {
private String bookName;
private String authorName;
}
We can use #ScriptAssert Annotation on Class BookDto:
#ScriptAssert(lang = "javascript", script = "_this.bookName != null || _this.authorName != null")
public class BookDto {
private String bookName;
private String authorName;
}
then in the resource/controller Class:
#PatchMapping("{id}")
public ResponseEntity<Book> updateAdvisor(#PathVariable("id") Integer id,
#Valid #RequestBody BookDto newBook) {
Book addedBook = bookService.updateBook(newBook);
return new ResponseEntity<>(addedBook,HttpStatus.OK);
}
Now #Valid annotation will validate whatever we have asserted in the #ScriptAssert annotation's script attribute. i.e it now checks if the body of a REST request is empty (e.g contains only {}).

How to effectively handle multiple user inputs with #RequestParam

I am using #RequestParam to catch the front-end user input and pass it to the back end through controller and save it to the database.
So far controller handles the request like this:
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(
#RequestParam("customerFirstName") String customerFirstName,
#RequestParam("customerLastName") String customerLastName,) {
Customer customer = customerService.saveCustomer(
customerFirstName, customerLastName);
return null;
}
Well I guess this is fine when I only have two #RequestParam for two arguements, but I am facing some table that has more than 10 params, I think by using #RequestParam is apparently not realistic, is there another around this?
You can save the customer directly.
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(Customer customer) {
customerService.saveCustomer(customer);
return null;
}
Spring can databind POJOs as long as you have a no-args constructor and setters for your properties.
You should create a provider to use JSON, so you can send complex Java\JS objects and not just primitives.
If you are going to deal with multiple #RequestParam you can always opt for a bean class approach.
Controller:
#RequestMapping(value = "/myURL", method = RequestMethod.POST)
public String saveCustomer(
#RequestBody Customer customer) {
return null;
}
Bean:
public class Customer{
private String customerFirstName;
private String customerLastName;
//constructors, getters and setters
}
Check this link out for more info on #RequestBody

Different validators for same model in different actions, #InitBinder and Spring Portlet MVC [duplicate]

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");
}
// ...
}
}

Categories