I am trying to perform some parameters validations on my rest controller, by using the JSR-303 bean validation. However, all validations are ignored when I annotate my method with the #HystrixCommand annotation.
For example :
#RestController
#Validated
public class TestController {
//This method ignore all validations :(, even if age < 10
#HystrixCommand(fallbackMethod = "fallback")
#RequestMapping(value = "/notok", method = RequestMethod.GET)
ResponseEntity<String> methodNotOk(
HttpServletRequest request,
#Min(10) #RequestParam(name = "age") final int age
) {
return ResponseEntity.ok("Age is " + age);
}
//This method will raised an error if age < 10, because of the age validation.
//This is the expected behavior
#RequestMapping(value = "/ok", method = RequestMethod.GET)
ResponseEntity<String> methodOk(
HttpServletRequest request,
#Min(10) #RequestParam(name = "age") final int age
) {
return ResponseEntity.ok("Age is " + age);
}
//Hystrix fallback
private ResponseEntity<String> fallback(HttpServletRequest request, #RequestParam("age") final int age, Throwable e) {
return ResponseEntity.badRequest().body("error");
}
}
For the methodNotOk, the #Min annotation is totally ignored, so the server can respond Age is 5, which is incorrect for this test case.
But, if I remove the #HystrixCommand, all go like clockwork.
So the question is how can I validate some method parameters when using #HystrixCommand ?
Thanks for the help
I have the same problem and I have not found anything on the internet.
I think the cause is the order that the Aspects are loaded (HystrixCommandAspect create a proxy of your Bean before that Validator is loaded), but I can not find a way to change it using the Java configuration of Spring
My last resort has been to move the logic to another Bean with #HystrixCommand, and then use the delegate pattern in the Controller
#RestController
#Validated
public class TestController {
#Autowired
private TestHystrixController delegate;
#RequestMapping(value = "/notok", method = RequestMethod.GET)
ResponseEntity<String> methodNotOk(HttpServletRequest request,
#Min(10) #RequestParam(name = "age") final int age) {
return delegate.methodNotOk(request, age);
}
}
#Controller
class TestHystrixController {
#HystrixCommand(fallbackMethod = "fallback")
ResponseEntity<String> methodNotOk(HttpServletRequest request,
final int age) {
return ResponseEntity.ok("Age is " + age);
}
}
Related
I'm trying to validate an email that I receive from the post request body but it's doesn't work !
#RequestMapping(value = "myservice/emails", method = RequestMethod.POST)
public String requestFoo(#RequestBody #Email String email) {
return email;
}
When I send a request with a string that doesn't respect the email regex the function is executed and I receive a 200 status code.
Even do I add the #Valid annotation the result is always the same.
#RequestMapping(value = "myservice/emails", method = RequestMethod.POST)
public String testValidation(#Valid #RequestBody #Email String email) {
return email;
}
Start with Spring 3.2 #RequestBody method argument may be followed by Errors object, hence allowing handling of validation errors in the same #RequestMapping :
#RequestMapping(value = "myservice/emails", method = RequestMethod.POST)
public ResponseEntity<String> testValidation(#Valid #RequestBody #Email String email, Errors errors) {
if (errors.hasErrors()) {
return ResponseEntity.badRequest().body(ValidationErrorBuilder.fromBindingErrors(errors));
}
return email;
}
And create a custom validator :
public class ValidationErrorBuilder {
public static ValidationError fromBindingErrors(Errors errors) {
ValidationError error = new ValidationError("Validation failed. " + errors.getErrorCount() + " error(s)");
for (ObjectError objectError : errors.getAllErrors()) {
error.addValidationError(objectError.getDefaultMessage());
}
return error;
}
}
For example, URL can be:
/api/groups?sdk&type=1
or
/api/groups?app&type=1
In java, I want to know the param in the url is sdk or app.
I have tried something like:
#RequestMapping(method = RequestMethod.GET)
public ResponseResult testGet(HttpServletRequest request, #RequestParam String sdk, #RequestParam int type) {
...
}
Basically you can have 2 methods and use the params variable in the #RequestMapping anotation to discriminate between the methods.
#RequestMapping(method = RequestMethod.GET, params="sdk")
public ResponseResult getSdk(HttpServletRequest request, #RequestParam int type) {
...
}
#RequestMapping(method = RequestMethod.GET, params="app")
public ResponseResult getApp(HttpServletRequest request, #RequestParam int type) {
...
}
You may or may not need to add the value = "/groups" to your request mapping, depending on how you have configured your class/app.
you can use a parameter for app and sdk so your url will be /api/groups?param=sdk&type=1 or /api/groups?param=app&type=1. you can find sample code below:
#RequestMapping(value = "/groups", method = RequestMethod.GET)
public RestResponse testGet(#RequestParam(value = "param", required = false) String param, #RequestParam(value = "type", required = false) String type) {
}
javax validation not working on method parameters.. This is a test code and none of javax validation works on method parameter...
#RequestMapping(value = "/{id}", method = RequestMethod.PUT, params = "action=testAction")
public Test update(
#Size(min = 1) #RequestBody List<String> ids,
#Min(3) #PathVariable String name) {
return doSomething(ids, name);
}
But i have class level validations which works perfectly...
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public RoleType create (#RequestBody #Validated(FieldType.class) User user) {
...
}
And
#Size(min = 2, max = 10, groups = { FieldType.class }, message = "Invalid user code")
public String getId() {
return _id ;
}
-- Solution --
all steps followed as per the accepted answer.
And another addition is annoation on class level
#Validated
class UserController
{
#RequestMapping(value = "/{id}", method = RequestMethod.PUT, params ="action=testAction")
public Test update(#Size(min = 1) #RequestBody List<String> ids,#Min(3) #PathVariable String name) {
return doSomething(ids, name);
}
}
you need to register MethodValidationPostProcessor bean to kick method level validation annotation
delegates to a JSR-303 provider for performing method-level
validation on annotated methods.
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
then,
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public Test update(
#Size(min = 1) #RequestBody List<String> ids,
#Min(3) #PathVariable("id") String name) {
return doSomething(ids, name);
}
if you want to handle validation exception
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
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");
}
// ...
}
}
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.