Spring MVC - validation for comparing two fields - java

I'm working on a Spring MVC application and have a question about spring validation. First, I have this action in my controller:
#RequestMapping(value = "/create", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody
Employee employeeCreate(#Valid #RequestBody EmployeeModelCreate objModel) throws Exception {
...
}
And this is my EmployeeModelCreate class:
public class EmployeeModelCreate implements Serializable {
...
#NotBlank(message = "...")
private String password;
#NotBlank(message = "...")
private String confirmPassword;
...
//Setters and Getters
}
Now, I want to have validation for comparing password and confirmPassword. This validation should be check equality of these two fields. How can I do that? Any help would be greatly appreciated.

You could make a custom validation class, for this you need to implement the Validator interface, then you can use it manually, or bind it to the Spring MVC controller, using the #InitBinder annotation.
This question may contain other useful information.

Related

spring rest #RequestBody does not validate with #Valid

I'm learning java and spring boot and I am trying to validate a controller parameter which was bound from json.
I've got simple Entity:
#Getter
#Setter
class Resource {
#NotBlank
String uri;
}
Which I want to persist through the following controller:
#BasePathAwareController
public class JavaResourcePostController {
private final ResourceRepository repository;
public JavaResourcePostController(ResourceRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/resources", method = RequestMethod.POST)
ResponseEntity<Resource> create(
#Valid #RequestBody Resource resource
) {
repository.save(resource);
return ResponseEntity.ok(resource);
}
}
My understanding is that the resource argument should be valid when entering the method. But posting an empty uri field does not trigger the validation of the method. it does however get triggered in the hibernate validation in repository.save()
Why does the #Valid annotation on the argument not ensure I get a validated entity?
You need to add #Validated to your controller class.

How should I validate dependent parameters in #requestparam in spring Rest API?

I know there are validators in spring. However, these validators can only be bound to a single object. Say a Pojo in request body. However, I have a scenario where I have a get request and I want to validate a date range: I have a start date and the end date as #requestparams. How should I validate these?
Also there is a validator applied for the same #restcontroller: for post request, say Employeevalidtor. Can I invoke multiple validators for different objects in the same #restcontroller?
You can use separate validators but they have to me manually instantiated by passing the corresponding objects to be validated.
I assume you are talking about request binding validations. The same validations can be obtained with Spring Validators for #RequestParam and #PathVariables as mentioned in this post
Adding the relevant piece here. The controller will look something like this:
#RestController
#Validated
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public Map search(#Email #RequestParam("email") String email) {
return emailMessage(email);
}
}
Note the #Validated method at the class level (which can also be declared at the method level).
Let Spring MVC will map your request parameters to a pojo encapsulating all the related inputs and then add a validator for that.
#RestController
#RequestMapping("/myUrl")
public class MytController {
private final MyIntervalValidator validator;
#InitBinder
public void initBinder(WebDataBinder binder){
binder.setValidator(validator);
}
#GetMapping
public void doSomthing(#Valid #RequestParam MyInterval interval){...}
class MyInterval implements Serializable{
private Date startDate;
private Date endDate;
}
import org.springframework.validation.Validator;
class MyIntervalValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return MyInterval.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final MyInterval params = (MyInterval) target;
....
}
}

Spring MVC - parameter binding

How come this code just works? I didn't specify any custom converter or annotation (like #RequestBody or #ModelAttribute) before argument ? Request is filled correctly from this GET call:
http://localhost:8080/WS/foo?token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
Code:
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(Request request) {
// ...
}
}
Request is just POJO with getters and setters. I use it to shorten argument code because plenty methods uses those same arguments ...
public class Request {
private String token;
#DateTimeFormat(pattern = IsoDateTime.DATETIME)
private Date lastSync;
private Integer pageNo;
// getters and setters
}
This was my original method before introducing Request.
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(#RequestParam String token, #RequestParam #DateTimeFormat(pattern = IsoDateTime.DATETIME) Date lastSync, #RequestParam Integer pageNo) {
// ...
}
}
Request parameters will be mapped to POJOs, as it is happening in your case, by default. Additionally, if you use #ModelAttribute, an attribute in the Model will be created. That attribute can be then used in views, e.g. JSPs, to access the object.
#RequestBody annotation tells that the body of the request is NOT a set of form parameters like
token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
but is in some other format, such as JSON.
This is a feature provided by Spring MVC:
Customizable binding and validation. Type mismatches as application-level validation errors that keep the offending value, localized date and number binding, and so on instead of String-only form objects with manual parsing and conversion to business objects.
You can see it in the doc: http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/htmlsingle/

Is it possible to validate #RequestParam in Spring MVC REST endpoint?

In Jersey 2 it is possible to do this:
#GET
#PATH("user/{email}")
public IDto getUser(#NotNull #Email #PathParam("email") String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
But I cannot make something similar to work in Spring MVC, it seems that the validation is only done when providing an object in #RequestBody or using an SpringMVC Form, for example the following won't work:
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
There are other similar questions, but those seem to be oriented to Spring MVC UI applications, in my case it is only a REST API which returns JSON response so I don't have any View to map/bind to the controller.
Seems it is possible, using #Validated.
Here's an example.
Based on OP's question, this should work:
#RestController
#Validated
public class MyController {
#GetMapping(value="/user/{email}")
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
}
In plain Spring implementations, it may be required to manually register the validator bean:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
1- Simply add #Validated annotation at the top of your class.
2- Put whatever annotations for validations (#NotBlank, Min(1), etc.) before the #RequestParam annotation in your method signature.
The validated annotation from the org.springframework.validation.annotation.Validated package to validate a #PathVariable. Make sure the class annotated with #Validated.
#GetMapping("/name-for-day/{dayOfWeek}")
public String getNameOfDay(#PathVariable("dayOfWeek") #Min(1) #Max(7) Integer dayOfWeek) {
return dayOfWeek + "";
}
As far as I can tell, you cannot do this out-of-the-box with Spring.
Options:
Use a regular expression:
#RequestMapping(value="/user/{email:SOME_REXEXP}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
Use Hibernate Validator to validate the method. Either call the validator manually, or make Spring call it for you using AOP. See https://github.com/gunnarmorling/methodvalidation-integration
Controller should be annotated with spring's #Validated
So update your code with
#Validated
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(
#NotNull
#Email
#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}

How to create a default method in SpringMVC using annotations?

I can't find a solution to this, and it's driving me crazy. I have #Controller mapped that responds to several methods using #RequestMapping. I'd like to tag one of those methods as default when nothing more specific is specified. For example:
#Controller
#RequestMapping("/user/*")
public class UserController {
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
So /user/login -> login method, /user/logout -> logout, etc. I'd like to make it so that if someone goes to /user then it routes to one of these methods. However, I don't see anything on #RequestMapping that would allow me to specify one of these methods as a default handler. I also don't see any other annotations that might be used on the class either to do this. I'm beginning to suspect it doesn't exist.
I'm using Spring 2.5.6. Is this solved in 3.0.0? I might just hack Spring to make it work because it's tremendously annoying this isn't more straightforward.
Thanks in Advance.
Take a look at this answer:
Spring MVC and annotated controllers issue
What if you annotate a method with:
#RequestMapping(method = RequestMethod.GET)
You can see an example here:
Spring 3.0 MVC + Hibernate : Simplified with Annotations – Tutorial
The same behavior can be seen here:
Spring Framework 3.0 MVC by Aaron Schram (look at page 21)
Short answer: I do not know how to simply specify one method as default with a simple tag.
But there is this ...
I do not know in which version of Spring this was implemented, but you can provide multiple values to #RequestMapping in 3.1.2. So I do this:
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = {"", "/", "/list"}, method = RequestMethod.GET)
public String listUsers(ModelMap model) { }
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) { }
}
The following URLs then map to listUsers():
http://example.com/user
http://example.com/user/
http://example.com/user/list
I would create one default method without RequestMapping's value in there. Please see method defaultCall() below. You can then simply call them with URL: [yourhostname]/user
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method = RequestMethod.GET)
public String defaultCall( MapModel model ) {
//Call the default method directly, or use the 'forward' String. Example:
return authenticate( model );
}
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
Ref: Spring Framework Request Mapping
Simply using #RequestMapping("**") on your default method should work. Any more specific mappings should still pick up their requests. I use this method for setting up default methods sometimes. Currently using Spring v4.3.8.RELEASE.

Categories

Resources