spring validation annotations being ignored when using custom validator - java

I am trying to incorporate annotated validation rules along with some custom validation. I have a details entity which looks like the following:
public class DetailsEntity {
#NotEmpty(message = "Name is required")
private String name;
private String customField;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCustomField() {
return customField;
}
public void setCustomField(String customField) {
this.customField = customField;
}
}
I then have a controller that looks like this:
#Controller
public class EntityController {
#RequestMapping(value = "/create", method = RequestMethod.POST)
public #ResponseBody DetailsEntity create(#RequestBody #Valid
DetailsEntity details) {
//Do some creation work
}
}
This all works great out of the box. The problem is when I try to use a custom validator along with my entity. My validator looks like this:
#Component
public class EntityValidator implements Validator {
#Override
public boolean supports(Class<?> aClass) {
return aClass.isAssignableFrom(DetailsEntity.class);
}
#Override
public void validate(Object o, Errors errors) {
DetailsEntity entity = (DetailsEntity) o;
if (entity.getCustomField().equals("Some bad value")) {
errors.reject("Bad custom value supplied");
}
}
}
I've tried injecting my validator two ways. One is using the #InitBinder in the controller, and the other is setting a global validator in the spring configuration (<mvc:annotation-driven validator="entityValidator" />). Either way I do it, the custom validator works fine, but my #NotEmpty annotation gets ignored. How can I use both the annotations as well as a custom validator?

Use SpringValidatorAdapter as base class of your custom validator and override validate() method:
public void validate(Object target, Errors errors) {
// check JSR-303 Constraints
super.validate(target, errors);
// Add you custom validation here.
}
Or inject a LocalValidationFactoryBean in you custom validator and call to validate(target, errors) before or after your custom validation.

#NotEmpty is a JSR-303 annotation, and we need to use an implementation of it like HiberanteValidator, we need to add Hibernate-Validator jar to your lib directory. Using this jar we can use #NotEmpty, #NotNull...all JSR 303 annotations.

Related

#JsonValue and #ApiModelProperty conflicts

in my project I am using Swagger to document REST API. I have simple value object which I want to document.
public class MyClass {
#JsonValue
private String myField;
public String getMyField() {
return myField;
}
}
Unfortunately, when I am adding swagger annotations and then see created documentation, there is no information about this VO.
Class with swagger annotations below:
#ApiModel(value = "MyClass ", description = "represents my class")
public class MyClass {
#JsonValue
#ApiModelProperty(value = "name", dataType = "String", example = "my field")
private String myField;
public String getMyField() {
return myField;
}
}
Wanted to check this issue I temporally removed #JsonValue annotation and documentation was created properly, annotation #ApiModelProperty worked.
I cannot remove #JsonValue annotation permanently.
Does anybody know the solution how can I force those two tools to cooperate?

Spring #ModelAttribute interface

How can I have an interface as a ModelAttribute as in the below scenario?
#GetMapping("/{id}")
public String get(#PathVariable String id, ModelMap map) {
map.put("entity", service.getById(id));
return "view";
}
#PostMapping("/{id}")
public String update(#ModelAttribute("entity") Entity entity) {
service.store(entity);
return "view";
}
Above snippet gives the follow errors
BeanInstantiationException: Failed to instantiate [foo.Entity]: Specified class is an interface
I don't want spring to instantiate entity for me, I want to use the existing instance provided by map.put("entity", ..).
As been pointed out in comments, the Entity instance does not survive between the get and post requests.
The solution is this
#ModelAttribute("entity")
public Entity entity(#PathVariable String id) {
return service.getById(id);
}
#GetMapping("/{id}")
public String get() {
return "view";
}
#PostMapping("/{id})
public String update(#ModelAttribute("entity") Entity entity) {
service.store(entity);
return "view";
}
What happens here is that the Entity in update binds to the Entity created from the #ModelAttribute annotated entity method. Spring then applies the form-values to the existing object.

How to create typesafe user roles for Spring Security?

I'd like to make use of spring-security with ROLE_ADMIN and ROLE_USER roles.
I therefore try to create a typesafe enum class, but the #Secured annotation requires a constant String, which I cannot achieve by using an enum class.
What could I change in the following code?
public enum UserRole {
ADMIN("ROLE_ADMIN");
private String role;
public UserRole(String role) {
this.role = role;
}
}
//error: The value for annotation attribute Secured.value must be a constant expression
#Secured(USerRole.ADMIN.value())
public class SecuredView {
}
This question is a bit old, but this is my take on it:
public enum Role implements GrantedAuthority {
ROLE_USER, ROLE_ADMIN;
#Override
public String getAuthority() {
return name();
}
}
You can then use this together with #PreAuthorize and Spring Expression Language to authorize your methods and classes like so:
#PreAuthorize("hasRole(T(<package name>.Role).ROLE_ADMIN)")
public void doSomeThing() {
...
}
Note: The package name has to be the entire package name (org.company.project) and without the < and >.
As you can see, this isn't type safe per definition, as SpEL expressions are still strings, but IDEs like IntelliJ recognizes them, and will let you know of any errors.
You can use #PreAuthorize with multiple roles using hasAnyRole().
Of course, this may become a bit verbose with many roles, but you can make it prettier by creating your own annotation like this:
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#PreAuthorize("hasRole(T(<package name>.Role).ROLE_ADMIN)")
public #interface AdminAuthorization {
}
Following this, you can authorize your methods like so:
#AdminAuthorization
public void doSomething() {
...
}
Partial solution:
public enum Role implements GrantedAuthority {
ADMIN(Code.ADMIN),
USER(Code.USER);
private final String authority;
Role(String authority) {
this.authority = authority;
}
#Override
public String getAuthority() {
return authority;
}
public class Code {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
}
}
Results in:
#Secured(Role.Code.ADMIN)
If you are using Lombok you can use the #FieldNameConstants annotation:
#FieldNameConstants
public class UserRoles {
private String ROLE_USER, ROLE_ADMIN;
}
And then use it like this:
#Secured(UserRoles.Fields.ROLE_ADMIN)
Maybe in the future, there will be the #EnumNameConstants annotation, which would fit even better.

Spring MVC and JSR 303

I'm using Spring 3 and JSR 303. I have a form backing object whose nested objects need to be validated. In the example below, how do I validate formObject.getFoo().getBean()? When I run the code below, the result parameter is always empty, even if the HTML page submits nothing, when the validation should fail. Note that it works(i.e. the validation fails) when I validate it manually by calling validate(formObject.getFoo().getBean(), Bean.class).
#Controller
public class FormController {
#RequestMapping(method = RequestMethod.POST)
public void process(HttpServletRequest request, #Valid FormObject formObject, BindingResult result) {
...
}
// This is the class that needs to be validated.
public class Bean {
#NotBlank
private String name;
}
public class Foo {
private Bean bean;
}
public class FormObject {
private Foo foo;
}
}
If you want validation to cascade down into a child object, then you must put the #Valid annotation on the field in the parent object:
public class Bean {
#NotBlank
private String name;
}
public class Foo {
#Valid
private Bean bean;
}
public class FormObject {
#Valid
private Foo foo;
}

Spring automatic binding for dropdown lists for bean properties

Is there a way to bind beans properties for another type of bean using the spring's form.select.
Example:
I have a bean that needs to be updated in the view with a property called BeanB:
public class BeanA {
private BeanB bean;
private int id;
private void setId(int id){
this.id = id;
}
private int getId(){
return this.id;
}
public void setBean(BeanB bean){
this.bean = bean;
}
public BeanB getBean(){
return this.bean;
}
}
public class BeanB{
private int id;
private void setId(int id){
this.id = id;
}
private int getId(){
return this.id;
}
}
For the view I want to send a list of BeanB to be chosen from using the spring's formcontroller:
public class MyController extends SimpleFormController{
protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) throws Exception {
BeanA bean = new BeanA();
//... init the bean or retrieve from db
List<BeanB> list = new ArrayList<BeanB>();
//... create list of objects
ModelAndView modelAndView = super.handleRenderRequestInternal(request, response);
modelAndView.getModel().put("beans", list);
modelAndView.getModel().put("bean", bean);
return modelAndView ;
}
}
In jsp I want to use a form.select to select the item I want to set for the BeanA from the given list, something like:
<form:select path="${bean.bean}" items="${beans}"/>
It looks like it doesn't work like this. Is there another simple solution for this?
To create the select markup in HTML:
<form:select path="bean" items="${candidates}" itemValue="id" itemLabel="name"/>
When the form is submitted, the value will be passed into Spring as a String, which will then need to be converted into a bean of the required type. Spring uses the WebDataBinder for this, using PropertyEditors to do the conversion to/from String. Since your 'id' attribute is probably already serializable as a String you are already seeing half of this working.
You're looking for this: http://static.springsource.org/spring/docs/2.5.6/reference/mvc.html#mvc-ann-webdatabinder
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(BeanB.class, new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
// some code to load your bean..
// the example here assumes BeanB class knows how to return
// a bean for a specific id (which is an int/Integer) by
// calling the valueOf static method
// eg:
setValue(BeanB.valueOf(Integer.valueOf(text)));
}
});
}
The docs for Spring 2.5.6 seems to suggest the #Controller and #InitBinder annotations work if configured, you'll have to extrapolate for your environment.
#see http://static.springsource.org/spring/docs/2.5.6/api/index.html?org/springframework/web/bind/WebDataBinder.html

Categories