Validatng enums in Spring validation - spring

I'm doing request parameter validation from a Spring controller. I have an Enum validator, similar to https://funofprograming.wordpress.com/2016/09/29/java-enum-validator/, which works fine if the enum field is directly in the object I'm using for validation. But it doesn't work if that object contains other objects.
For example, here is the request in the Controller
#PostMapping("/")
public ResponseEntity<?> performOperation(#Valid #RequestBody MyModel model) {
Here is the model I'm using to validate the request params
#ApiModel
public class MyModel {
#ApiModelProperty
#EnumValueValidator(enumClass = EnumName.class)
public String provider;
MyObject obj;
}
public class MyObject {
#EnumValueValidator(enumClass = SomeEnum.class)
public String anotherEnum;
}
In the above example, provider is validated with no problem. But anotherEnum is not. Is there a way for a Spring model to do a deep validation into objects?

You should annotate MyObject obj with #Valid annotation as well. Just keep in mind that null objects are not validated, so probably you should do both:
#NotNull
#Valid
MyObject obj;

Related

Spring HATEOAS recursive representation model processors?

I have a question concerning the representation model processors of Spring HATEOAS. We are experimenting to process models before serializing them to the client. Our use case is to enrich the imageUrl field of UserModel objects at runtime, as we have to build the URL based on values from a config bean (AWS S3 bucket URL differs for DEV / PROD setup).
#Data
public class UserModel {
// ...
private String imageUrl;
}
Therefore, we create a UserProcessor to implement this:
public class UserProcessor implements RepresentationModelProcessor<EntityModel<UserModel>> {
private final ConfigAccessor configAccessor;
public UserProcessor(ConfigAccessor configAccessor) {
this.configAccessor = configAccessor;
}
#Override
public EntityModel<UserModel> process(EntityModel<UserModel> model) {
if (model.getContent() != null)
// do the enrichment and set "imageUrl" field
}
return model;
}
}
This works perfectly if we have a controller method like this:
#ResponseBody
#GetMapping("/me")
public EntityModel<UserModel> getCurrentUser(#AuthenticationPrincipal Principal principal) {
UserModel user = ... // get user model
return EntityModel.of(user);
}
However, we are struggling now with the enrichment whenever a UserModel is referenced in another model class, e.g., the BookModel:
#Data
public class BookModel {
private String isbn;
// ...
private EntityModel<UserModel> user; // or "private UserModel user;"
}
A controller method returning type EntityModel<BookModel> only applies the processor for its type, but not for types that are referenced. It seems the processors are not applied recursively.
Is this intentional or are we doing something wrong?
Thanks for any input and help,
Michael
I encountered the same issue and I resolved it by manually assembling resources, in your case that would be implementing RepresentationModelAssembler of the BookModel and then manually invoking the processor on the userModel object that is inside the book.
Make the outer resource a representation model
First consider the BookModel to extend RepresentationModel so that you can manually add links and assemble inner resources (which you would like for the EntityModel<UserModel> object)
#Data
public class BookModel extends RepresentationModel<BookModel> {...}
Write a model assembler
Now write the assembler that takes your book entity and transforms it into a representation model or a collection of these models. You will implement here what EntityModel.of(...) does for you automagically.
#Component
public class BookModelAssembler implements RepresentationModelAssembler<Book, BookModel> {
#Autowired
private UserProcessor userProcessor;
#Override
public BookModel toModel(Book entity) {
var bookModel = new BookModel(entity) // map fields from entity to model
// Transform the user entity to an entity model of user
var user = entity.getUser();
EntityModel<UserModel> userModel = EntityModel.of(user);
userModel = userProcessor.process(userModel);
bookModel.setUserModel(userModel);
return bookModel;
}
}
I might be going out on a limb but I suppose the reason for this is that the processors get invoked when an MVC endpoint returns a type that has a registered processor, which in the case of embedded types is not invoked. My reasoning is based on the docs for RepresentationModelProcessor, which states that processor processes representation models returned from Spring MVC controllers.

How to send model attribute to spring controller

I want to test my controller using postman but don't know how to send a model attribute using postman. I even don't know whether it is possible or not.
My Controller seems like:
#Controller
#RequestMapping(path = "/api/v1")
public class PaymentController {
#Autowired
private CredentialsRepository credentialsRepository;
#PostMapping(path = "/charge")
public String charge(#ModelAttribute("pay-load") PayLoad payLoad, Model model) {
Credentials creds = credentialsRepository.findCredentialsById(1);
if (creds == null)
return "init_credentials";
return "charge";
}
}
Model Attribute
public class PayLoad {
private Integer mId;
private Integer ordId;
private Integer cardId;
private Integer cvvNo;
private String hash;
// getter & setter
}
I found the way to send model attributes to the spring controller.
see above screenshot for your reference.
Even you can pass all the key and value in requestParam from postman.
Instead of requestBody. ModelAttribute object treat each and every key and value as requestParam. It's just a way to combine a lot of requestParam in one object. Even you can try out with curl request, so it will make you more clear.
Thanks

Custom serialization of single #RestController endpoint

Is there a way (preferably some type of annotation) to register a custom serializer for a single endpoint in a #RestController? Extending the bean and putting a #JsonSerialize on it would be an option, but that demands an otherwise pretty useless new bean class. I tried the following:
#JsonSerialize(using = CustomSerializer.class)
#RequestMapping(value = "/some_endpoint/", method = RequestMethod.GET)
public SomeType someEndpoint() {
return someObject;
}
But the #JsonSerialize annotation doesn't appear to have any meaning to Spring in that context. Is there an alternative or is the extra bean class my only option?
You can use #JsonView(View.Summary::class) in the attributes you want to add or ignore and in the method you want to apply that view, for example:
public class View {
public interface Summary
}
public class A{
#JsonView(View.Summary.class)
private String serialized = "",
private String notSerialized = ""}
and then in the controller:
#JsonView(View.Summary.class)
#GetMapping("/")
#ResponseBody
public A getA(){
return A()
}
If you want to reverse the JsonView (serialize the atributtes who doesnt have the view). you can add the following propertie: spring.jackson.mapper.default-view-inclusion=true

spring: hook between data binding and validation

I have a Roo generated application and use validation, spring security with a custom PermissionEvaluator and the generated web controllers. My entity has a field like this:
#NotNull
private Date creationDate;
This field is set automatically inside the controller's create method when saving the entity and is not included in the form (render="false").
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
#PreAuthorize("hasPermission(#myEntity, 'create')")
public String create(#Valid MyEntity myEntity,
BindingResult bindingResult, Model uiModel,
HttpServletRequest httpServletRequest) {
// ... check binding result
myEntity.setCreationDate(new Date());
myEntity.persist();
// ...
}
The problem is, that validation always fails because it runs before the field is set. The PermissionEvaluator (called by #PreAuthorize) is also missing the value. How can I place my code somewhere between data binding and validation so that the entity is complete right from the beginning?
To solve the problem of #PreAutorize move the persistence logic to a #Service bean and call it from the controller. This way security check will be after validation. Roo can help you on it with service command.
Second, you can use validation groups to make different validation on for the same entity. This and this are two howto post.
An example:
#RequestMapping("/myEntity")
public MyEntityController {
#Autowired
MyEntityService myEntityService;
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(#Validated({Account.ValidationStepOne.class}) MyEntity myEntity,
BindingResult bindingResult, Model uiModel,
HttpServletRequest httpServletRequest) {
// ... check binding result
myEntityService.save(myEntity);
//...
}
}
#Service
public MyEntityService {
#PreAuthorize("hasPermission(#myEntity, 'create')")
public save(MyEntity myEntity) {
//...
myEntity.setCreationDate(new Date());
myEntity.persist();
}
}
Good luck!

Spring MVC : Common param in all requests

I have many controllers in my Spring MVC web application and there is a param mandatoryParam let's say which has to be present in all the requests to the web application.
Now I want to make that param-value available to all the methods in my web-layer and service-layer. How can I handle this scenario effectively?
Currently I am handling it in this way:
... controllerMethod(#RequestParam String mandatoryParam, ...)
and, then passing this param to service layer by calling it's method
#ControllerAdvice("net.myproject.mypackage")
public class MyControllerAdvice {
#ModelAttribute
public void myMethod(#RequestParam String mandatoryParam) {
// Use your mandatoryParam
}
}
myMethod() will be called for every request to any controller in the net.myproject.mypackage package. (Before Spring 4.0, you could not define a package. #ControllerAdvice applied to all controllers).
See the Spring Reference for more details on #ModelAttribute methods.
Thanks Alexey for leading the way.
His solution is:
Add a #ControllerAdvice triggering for all controllers, or selected ones
This #ControllerAdvice has a #PathVariable (for a "/path/{variable}" URL) or a #RequestParam (for a "?variable=..." in URL) to get the ID from the request (worth mentioning both annotations to avoid blind-"copy/past bug", true story ;-) )
This #ControllerAdvice then populates a model attribute with the data fetched from database (for instance)
The controllers with uses #ModelAttribute as method parameters to retrieve the data from the current request's model
I'd like to add a warning and a more complete example:
Warning: see JavaDoc for ModelAttribute.name() if no name is provided to the #ModelAttribute annotation (better to not clutter the code):
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name:
e.g. "orderAddress" for class "mypackage.OrderAddress",
or "orderAddressList" for "List<mypackage.OrderAddress>".
The complete example:
#ControllerAdvice
public class ParentInjector {
#ModelAttribute
public void injectParent(#PathVariable long parentId, Model model) {
model.addAttribute("parentDTO", new ParentDTO(parentId, "A faked parent"));
}
}
#RestController
#RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
#GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(#ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}
To continue about the warning, requests are declaring the parameter "#ModelAttribute ParentDTO parent": the name of the model attribute is not the variable name ("parent"), nor the original "parentId", but the classname with first letter lowerified: "parentDTO", so we have to be careful to use model.addAttribute("parentDTO"...)
Edit: a simpler, less-error-prone, and more complete example:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
public #interface ProjectDependantRestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
*
* #return the suggested component name, if any
*/
String value() default "";
}
#ControllerAdvice(annotations = ParentDependantRestController.class)
public class ParentInjector {
#ModelAttribute
public ParentDTO injectParent(#PathVariable long parentId) {
return new ParentDTO(parentId, "A faked parent");
}
}
#ParentDependantRestController
#RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
#GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(#ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}

Resources