I am trying to write a custom validator in a Spring MVC application. I would like to know if there a way to pass the #Model object to a custom spring validator?
Say, I have a Person object and Account object. I have to write a custom validator to validate Person, but the validation is dependent of the Account object and other objects in session.
For example, Person cannot have more than 3 accounts, account types have to be of specific category and not old than 3 years (this value, ie the number years is dynamic based on the profile logged in and is in session).
How can I pass both objects, especially #Model to the validator.
public class ServiceValidator implements Validator {
#Autowired
private ServiceDAO servicesDao;
#Override
public boolean supports(Class<?> clasz) {
return clasz.isAssignableFrom(Person.class);
}
#Override
public void validate(Object obj, Errors errors) {
Person subscriber = (Person) obj;
// How can I access #Model object here ???
}
Doubt if you can but have two workarounds:
a. If it is persisted data that you are looking for, probably it is just better to retrieve it once more in the validator and validate using that data, so for eg, in your case if you are validating person and persons account details are retrievable from DB, then get it from DB and validate in your validator using the retrieved data.
b. Probably this is a better approach if the number of places where you need to use the validator is fairly confined:
public class ServiceValidator {
#Autowired
private ServiceDAO servicesDao;
public void validate(Person subscriber, List<Account> accounts, ..., Errors errors) {
}
Just call the above validator directly from your requestmapped methods..
In your controller..
List<Account> accounts = //retrieve from session
serviceValidator.validate(subscriber, accounts, ...errors);
if (errors.hasErrors())..
else..
Related
Scenario:
A community webapp where people can form communities about certain topics with a Spring REST backend.
Today I was wondering how one would implement a setting akin "Who can see your email adress".
When a User enters a community and a REST call to e.g. /api/community/1/users is being made, how would it be possible to stop the CrudRepository from serializing a field such as email of User B if the user A making the API call to the backend is not a friend / does not fulfill certain criteria of user B's settings, such as only showing emails to approved contacts. The resulting JSON should contain a list of users with some having a email field and some not.
While searching I was not able to find anything that matches my question. Following are some things I have discovered but don't feel like they are of much help.
Annotating Controller methods / Repository methods with #PreAuthorize, passing in the Principal.
Why I think this might not help: This seems to be a good solution if I want to block someone based on their ID from viewing a ressource completely. Such as Users only being able to see their own data but not others because the Principal ID does not match the requested ressource's id.
Using JsonFilter as described here: https://www.baeldung.com/jackson-serialize-field-custom-criteria
With this approach I don't see a way of checking WHO is making a request for e.g. my email.
This approach seems to fit well for a scenario such as having a boolean flag set to show email or not, for all cases and any requesters.
Creating a new domain object such as "Friend" extending "User", which is only there for overwriting the #JsonIgnore property of User. While a normal User would not have their Email field serialized due to #JsonIgnore, a friend would set #JsonIgnore(false) over email.
I dont like this approach because I feel like it must somehow be possible to implement this functionality without creating new classes only to overwrite Jackson annotations.
Sorry if there isn't any code to show. So far I have only been creating simple entities and mostly theorycrafting how it would be possible to accomplish the above when I saw that the repository exposes everything. I'm usually more home at the Frontend side of things but I want to learn backend with Spring as well, for private as well as professional reasons. I hope the question isn't too basic.
Thank you in advance.
You can use #JsonView from Jackson for it.
First, create a DTO with the fields you want to return and annotate them with #JsonView:
public class UserDto {
#JsonView(NoFriend.class)
private String name;
#JsonView(Friend.class);
private String email;
public static class NoFriend {}
public static class Friend extends NoFriend {}
}
The NoFriend and Friend inner classes are just markers to define what fields should be returned in what case.
Now in your controller, instead of returning a UserDto, you wrap the UserDto in a MappingJacksonValue:
public class UserController {
#GetMapping("/api/community/1/users")
public List<MappingJacksonValue> getUsers(#AuthenticationPrincipal Principal principal) {
List<User> users = service.getUsers();
return users.stream()
.map( user -> {
MappingJacksonValue value = new MappingJacksonValue(UserDto.fromUser(user));
value.setSerializationView(getView(principal, user));
})
.collectors(toList());
}
private Class getView(Principal princapl, User user) {
// return UserDto.Friend.class or UserDto.NoFriend.class, depending the relation of the authentication principal with the user
}
Probably, not the simplest way to implement it. But maybe it will help you to decompose a problem and find an appropriate solution.
I assume that you just want to clear fields on API level, but still gonna fill it in your Objects.
Let's define a model with some security metadata on it:
class UserDTO {
Long id;
String name;
#AllowOnly("hasRole('FRIEND')") // SPeL/any your custom defined language, or simpler:
//#AllowOnly(Role.FRIEND)
String email;
}
Then define a controller class
#RestController
class SomeController {
#GetMapping("/api/community/{id}/users")
public List<UserDTO> getUsers() {
return List.of(
new UserDTO(1, "Bob", "email-you#gonna.see"),
new UserDTO(2, "Jack", "email-you-NOT#gonna.see"))
}
}
So what i propose is to create an aspect, which is gonna clear fields based on your permission model.
#AfterReturning("within(#org.springframework.web.bind.annotation.RestController *)
&& execution(* *(..))", returning="objectToClear")
public void applyFieldPermissions(Object objectToClear) {
// Here i would parse annotations on object fields
// and if found #AllowOnly, check your role to a user.
// and clean up field, if necessary
}
Logic of the aspect is totally dependent on your cases, but for this simple example, need only to implement some method to check your role for specific object
boolean hasRoleOn(UserDto dto, Role role, Authentication currentUser)
I have some validation logic, which is based a user's session.
Сan I access the session from a class inherited from AbstractValidator?
Or is there another way to check RequestDTO based session?
For example I can use filters for that (and I can access the session via IRequest parameter req in overrided Execute method), but I don't know how to return the response from the class of the filter, bypassing the main method of the operation. If I will throw the Exception, the service will not work properly because RequestDTO declared outside of my project and it is not inherited from IReturn<> and does not contain a ResponseStatus field.
Thanks in advance for any help (and sorry for my English)
If your validator implements IRequiresRequest then it will injected with the current IRequest which you can use in your lambda expressions, e.g:
public class CustomValidator : AbstractValidator<Request>, IRequiresRequest
{
public IRequest Request { get; set; }
...
}
The data I work with can be added from different interfaces. So in my MVC application I need to validate that the data read from the database is correct according to the rules/attribute I sat in my model. Depending on the validation send the user to different Views.
I need to do this validation on the SERVER-SIDE
var myModelClassObj = myDbContextInheritedModelClass.theDbSetClassMapToModel.Find(123);
// How do i validat this
The DbContext class has a protected ValidateEntity method. You can call that method to validate your entity.
public class myDbContextInheritedModelClass : DbContext
{
public DbEntityValidationResult Validate(object entity)
{
return ValidateEntity(Entry(entity));
}
}
Then
var myModelClassObj = myDbContextInheritedModelClass
.theDbSetClassMapToModel.Find(123);
var validationResult = myDbContextInheritedModelClass.Validate(myModelClassObj);
So I've found a bunch of Struts 2 CRUD examples around the web:
Struts 2 CRUD Demo
and a few books:
Apache Struts 2 Web Application Development ISBN: 978-1847193391
Struts 2 Design and Programming ISBN: 978-0980331608
But all of them differ a little bit on how to do form population.
Some suggest implementing the Java interfaces ModelDriven or Prepareable to call come prepare function to pre-populate any needed data members.
Others suggest creating your own PrepareForUpdate action that calls a pre-populate function then redirects to the main edit view.
They also very on how to pass around an object identifier to indicate what object to retrieve for editing. SOme suggest intercepters what others throw it in the URL parameters and retrieve it through ActionContext or pass it around through a s:hidden field.
Is there a Best Practices way to do form population in Struts 2?
What are the advantages/disadvantages to the methods mentioned above?
I'm not aware of any documented best practices, but I've been using Webwork and Struts2 for about three years now, so I can tell you what I've used in my projects. By the way, the CRUD demo documentation you linked to strikes me as a bit out of date (I realize its from the project site).
I split my CRUD work into three different actions:
An action that lists the entities. It supports pagination and populates some type of a table or grid view.
An action that handles both add and edit functionality. Uses a prepare() method to set up dropdowns, etc.
An action that handles delete functionality.
Some suggest implementing the Java interfaces ModelDriven or Prepareable to call come prepare function to pre-populate any needed data members.
That's the approach that I would advocate, although I don't use the ModelDriven interface. For details, check out how Struts2 ModelDriven interface works and the comments on my answer. Whether you use ModelDriven or not is just a personal choice. Also, check out why is model-driven action preferred over object backed bean properties.
Others suggest creating your own PrepareForUpdate action that calls a pre-populate function then redirects to the main edit view.
I have not seen that before and based on your description, I would avoid that technique. It seems wasteful to do a redirect and create an additional HTTP request to achieve the same thing that the prepare() method was designed to handle.
They also very on how to pass around an object identifier to indicate what object to retrieve for editing.
Just pass the identifier in the URL or the form. That's the standard approach for web applications.
I've been using Struts 2 for about 3 years. I use ModelDriven and Prepareable together in the same action. Each domain object (model) has a struts action class that returns a list or single object depending on if the id was passed to the action. This works pretty well for me, and the only time it's been problematic is when using Ajax. I usually separate my Ajax actions into a separate action for the model, if I am using them. I store the model id, as well as any related objects that I might need as hidden HTML fields in the view.
Using this approach, the action and the view are restful. You can leave the page for a long period of time and invoke the action without fear that the action will fail. Here's an example:
public class ApplicationAction extends MyBaseAction
implements ModelDriven<Application>, Preparable {
private static final long serialVersionUID = 7242685178906659449L;
private ApplicationService applicationService;
private Application application;
private Integer id;
List<Application> allApplications;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Application getModel() {
return application;
}
public void prepare() throws Exception {
if(id == null || id.intValue() == 0){
application= new Application();
}else{
application= applicationService.getApplication(id);
}
}
#SkipValidation
public String list() throws Exception {
allApplications = applicationService.getApplications();
return SUCCESS;
}
#Validations( visitorFields = {#VisitorFieldValidator(message = "Validation Error", fieldName = "model", appendPrefix = false)})
public String update() throws Exception {
applicationService.saveApplication(application);
addActionMessage("Application Saved Successfully.");
return SUCCESS;
}
public void setApplicationService(ApplicationService applicationService) {
this.applicationService = applicationService;
}
public List<Application> getAllApplications() {
return allApplications;
}
}
I'm working with Spring MVC and I'd like it to bind a a persistent object from the database, but I cannot figure out how I can set my code to make a call to the DB before binding. For example, I'm trying to update a "BenefitType" object to the database, however, I want it to get the object fromthe database, not create a new one so I do not have to update all the fields.
#RequestMapping("/save")
public String save(#ModelAttribute("item") BenefitType benefitType, BindingResult result)
{
...check for errors
...save, etc.
}
There are several options:
In the simpliest case when your object has only simple properties you can bind all its properties to the form fields (hidden if necessary), and get a fully bound object after submit. Complex properties also can be bound to the form fields using PropertyEditors.
You may also use session to store your object between GET and POST requests. Spring 3 faciliates this approach with #SessionAttributes annotation (from the Petclinic sample):
#Controller
#RequestMapping("/owners/*/pets/{petId}/edit")
#SessionAttributes("pet") // Specify attributes to be stored in the session
public class EditPetForm {
...
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
// Disallow binding of sensitive fields - user can't override
// values from the session
dataBinder.setDisallowedFields("id");
}
#RequestMapping(method = RequestMethod.GET)
public String setupForm(#PathVariable("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet); // Put attribute into session
return "pets/form";
}
#RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
public String processSubmit(#ModelAttribute("pet") Pet pet,
BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "pets/form";
} else {
this.clinic.storePet(pet);
// Clean the session attribute after successful submit
status.setComplete();
return "redirect:/owners/" + pet.getOwner().getId();
}
}
}
However this approach may cause problems if several instances of the form are open simultaneously in the same session.
So, the most reliable approach for the complex cases is to create a separate object for storing form fields and merge changes from that object into persistent object manually.
So I ended up resolving this by annotating a method with a #ModelAttribute of the same name in the class. Spring builds the model first before executing the request mapping:
#ModelAttribute("item")
BenefitType getBenefitType(#RequestParam("id") String id) {
// return benefit type
}
While it is possible that your domain model is so simple that you can bind UI objects directly to data model objects, it is more likely that this is not so, in which case I would highly recommend you design a class specifically for form binding, then translate between it and domain objects in your controller.
I'm a little confused. I think you're actually talking about an update workflow?
You need two #RequestMappings, one for GET and one for POST:
#RequestMapping(value="/update/{id}", method=RequestMethod.GET)
public String getSave(ModelMap model, #PathVariable Long id)
{
model.putAttribute("item", benefitDao.findById(id));
return "view";
}
then on the POST actually update the field.
In you example above, your #ModelAttribute should already be populated with a method like the above method, and the properties be bound using something like JSTL or Spring tabglibs in conjunction with the form backing object.
You may also want to look at InitBinder depending on your use case.