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;
}
Related
On a spring boot 2.4.3 application with Java:
I am using a DTO to construct the JSON response from the domain model of the application. The DTO is just a plain java object.
I am trying to property inject a new class that I created for data transformation using the #Autowired but I get a nullPointerException on the runtime.
#Data
#NoArgsConstructor
public class FetchSupplierDTO {
private long id;
private String name;
private String description;
private String info;
private List<String> tags;
#Autowired
private TagTranslation tagTranslation;
public FetchSupplierDTO(SupplierEntity supplier) {
this.id = supplier.getId();
this.name = supplier.getDisplayName();
this.description = supplier.getGivenDescription();
this.info = supplier.getInfo();
if (supplier.getTags() != null) {
this.tags = tagTranslation.extractTagsFromEntity(supplier.getTags());
}
}
}
#Service
public class TagTranslation {
public List<String> extractTagsFromEntity(List<TagEntity> tagEntityList) {
List<String> tagStringList = new ArrayList<>();
tagEntityList.forEach(productTag -> { tagStringList.add(productTag.getTag()); });
return tagStringList;
}
}
First of all, looking at the current code I would design it so that the caller of the constructor is responsible for calling the autowired service. Then the DTO really stays a DTO and is not at the same time responsible for calling a service.
If there is really no way around calling a Spring component from inside a DTO then you will have to get the component manually. And call SpringBeanLocator.getBean(TagTranslation.class) to get that component and insert it in your field.
Spring holds a single instance of each component, on initialization it scans for autowired annotations within annotated classes (#Component, #Service) and initializes those fields. Once you call the constructor of such a class separately, it will not return the instance that is maintained by Spring, it will construct a new instance. Therefore it's autowired fields will be null.
public class SpringBeanLocator implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public final void setApplicationContext(ApplicationContext context) {
validate(getInternalContext());
setContext(context);
}
public static <T> T getBean(Class<T> type) {
context.getBean(type);
}
}
I have a controller which produces JSON, and from this controller, I return an entity object, which is automatically serialized by Jackson.
Now, I want to avoid returning some fields based on a parameter passed to the controller. I looked at examples where this is done using FilterProperties / Mixins etc. But all the examples I saw requires me to use ObjectMapper to serialize / de-serialize the bean manually. Is there any way to do this without manual serialization? The code I have is similar to this:
#RestController
#RequestMapping(value = "/myapi", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(value = "/test/{variable}",method=RequestMethod.GET)
public MyEntity getMyEntity(#PathVariable("variable") String variable){
return myservice.getEntity(variable);
}
}
#Service("myservice")
public class MyService {
#Autowired
private MyEntityRepository myEntityRepository;
public MyEntity getEntity(String variable){
return myEntityRepository.findOne(1L);
}
}
#Entity
#Table(name="my_table")
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyEntity implements Serializable {
#Column(name="col_1")
#JsonProperty("col_1")
private String col1;
#Column(name="col_2")
#JsonProperty("col_2")
private String col2;
// getter and setters
}
Now, based on the value of "variable" passed to the controller, I want to show/hide col2 of MyEntity. And I do not want to serialize/deserialize the class manually. Is there any way to do this? Can I externally change the Mapper Jackson uses to serialize the class based on the value of "variable"?
Use JsonView in conjunction with MappingJacksonValue.
Consider following example:
class Person {
public static class Full {
}
public static class OnlyName {
}
#JsonView({OnlyName.class, Full.class})
private String name;
#JsonView(Full.class)
private int age;
// constructor, getters ...
}
and then in Spring MVC controller:
#RequestMapping("/")
MappingJacksonValue person(#RequestParam String view) {
MappingJacksonValue value = new MappingJacksonValue(new Person("John Doe", 44));
value.setSerializationView("onlyName".equals(view) ? Person.OnlyName.class : Person.Full.class);
return value;
}
Use this annotation and set the value to null, it will not be serialised:
#JsonInclude(Include.NON_NULL)
How can I change validation rules based on which class given bean is enclosed in?
Example:
public class ParentA {
#Valid
private Child c;
}
public class ParentB {
#Valid
private Child c;
}
public class Child {
#NotNull // when in ParentA
#Null // when in ParentB
private String name;
}
There are validation groups, however, I do not know how to apply them in this case. Can I specify the following: if validating ParentA then apply GroupA for its fields, hopefully by some annotation and without instanceof? I really do not want to create two types ChildA and ChildB with different validation annotations. I am building REST service with spring 4. Thanks for any feedback.
Try this code
public class ParentA {
#Valid
#ConvertGroup(from=Default.class, to=ParentA.class)
private Child c;
}
public class ParentB {
#Valid
#ConvertGroup(from=Default.class, to=ParentB.class)
private Child c;
}
public class Child {
#NotNull(groups=ParentA.class)
#Null(groups=ParentB.class)
private String name;
}
You can find more info and other examples in hibernate validator reference guide
I have implemented my validation for list of custom class as mention in this post. For reference here my code looks like
class TopDtoForm {
#NotEmpty
private String topVar;
private List<DownDto> downVarList;
//getter and setter
}
class DownDto {
private Long id;
private String name;
//getter and setter
}
#Component
public class TopDtoFormValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return TopDtoForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
TopDtoForm topDtoForm = (TopDtoForm) target;
for(int index=0; index<topDtoForm.getDownVarList().size(); index++) {
DownDto downDto = topDtoForm.getDownVarList().get(index);
if(downDto.getName().isEmpty()) {
errors.rejectValue("downVarList[" + index + "].name", "name.empty");
}
}
}
}
So even I send empty name binding result has 0 error. I tested with topVar and it is working fine. My question is do I have to do any other configuration to say use this validator?
Thanks
In Spring MVC just annotate in TopDtoForm your list with #Valid and add #NotEmpty to DownDto. Spring will validate it just fine:
class TopDtoForm {
#NotEmpty
private String topVar;
#Valid
private List<DownDto> downVarList;
//getter and setter
}
class DownDto {
private Long id;
#NotEmpty
private String name;
//getter and setter
}
Then in RequestMapping just:
#RequestMapping(value = "/submitForm.htm", method = RequestMethod.POST) public #ResponseBody String saveForm(#Valid #ModelAttribute("topDtoForm") TopDtoForm topDtoForm, BindingResult result) {}
Also consider switching from #NotEmpty to #NotBlank as is also checks for white characters (space, tabs etc.)
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.