I have a private field name on an Entity class: Product, which doesn't have a setName() method due to the business logic not allowing names to be changed.
I have an endpoint of the form:
#RequestMapping(value = "/endpoint", method = POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Product saveProduct(
#Validated(FormSubmission.class) Product product,
Errors errors,
#RequestParam("sellerId") long sellerId) { ... }
I was under the impression that Spring was able to populate private fields as well when parsing request bodies but the name property stays null after a correctly formatted POST request unless I add a setName() method to Product.
Is the setter a requirement?
The private modifier specifies that the member can only be accessed in its own class.
and if you are outside, you can't read/write.
Related
If I have the DTO object, added as attribute model in controller. It has two business fileds, say
public class Owner {
private Long id;
private String firstName;
private String secondName;
}
in some spring form I'd like to change the first name only still using the Owner as DTO, and this form doesn't have the second name input at all. And I also don't want to place the second name to the form as hidden input for privacy reasons, for example, so I have the only input dealing with the first name in the form? Is there a way to place the DTO to the model with both first and second name and in the #PostMapping controller method still getting both first and second name in the #ModelAttribute parameter object of the method? i'm getting the second name null in this case.
If the model was added to model attribute via addAttribute("owner", owner), #SessionAttribute("owner") annotation over the #Controller-marked class together with using #ModelAttribute("owner") ownet in the controller method parameter is the decision that helps. With #SessionAttribute the data is not cleared by the form with all fields put to null values.
Explicit naming of #ModelAttribute("named_via_here") being the same as #SessionAttribute("named_via_here") is required (it may work without it, but not guaranteed)
Here s my CODE to start with:
PersonController.java
#RequestMapping(value = "/person", method = RequestMethod.POST)
public ResponseEntity<?> addPerson(#Valid Person p, HttpServletResponse response) {
...
}
Person.java
public class Person {
#NotNull
String name;
#NotNull
int age;
String gender;
}
The requirement is: When a POST request is made to /person, I want an exception to be thrown if the user did not specify a key for the string Name in the BODY of the request. The annotation #NotNull does not do this.
Is there another annotation that I can use in Person.java to achieve this? If not, is there some validation I could do in the addPerson method to ensure that an exception is thrown if one of the mandatory parameters are not there?
Actually the #NotNull annotation does exactly what you want but unfortunately it can't do it on int type since it can't be null. In order for it to work you need to change the age to Integer and then after the spring does the binding of values if both parameters are passed and they have values the validation will pass. Otherwise if they are passed with empty value or not passed at all the value will be null and the validation will fail. Just make sure that you don't have some constructor for Person that initializes the attributes to some values.
If you don't want to change it and use an int you can add HttpServletRequest request to the method arguments and check if there is a parameter age present with:
request.getParameter('age');
If it is null then no parameter was passed at all.
Hint: It may be that you are missing some configuration and the annotation are not processed at all, something like <mvc:annotation-driven/> or #EnableWebMvc or maybe you are missing an actual validator implementation like Hibernate Validator. It is hard to tell without a sample of your configuration.
First, you need to encapsulate the fields in your domain-classes. The spring container will use these getters and setters to manipulate the object.
Then, you can add constraints to these getters and setters (Bean Validation). If you added them correctly, Spring will catch errors when using the #Valid annotation (which you did). These errors will be added to the BindingResult, and can be shown in a jsp by using the Spring form tags.
<form:errors path="field_that_was_manipulated_incorrectly" />
#RestController
#RequestMapping(value = "/players")
public class REST {
#RequestMapping(method = RequestMethod.GET)
public List<Player> getAll() {
return service.getAll();
}
#RequestMapping(method = RequestMethod.GET, params = {"team", "score"})
public List<Player> getByPlayerAndScore(#RequestParam(value = "team") String team,
#RequestParam(value = "score", required = false) int score) {
return service.getByPlayerAndScore(team, score);
}
}
Q1: I am expecting first method to work for url "/players" (worked as expected) and second method to work for url's ("/players?team=xyz", "/players?team=xyz&score=1000"). spring used method1 for "/players?team=xyz". Even i specified score as optional, unless i specify 2 params, spring is is not using 2nd method. How to solve this and what is best way of writing controller methods to handle these type of requests where user can send different sets of available params (like param1¶m2, only param1, only param2 etc).
Q2: For the 2nd type of query with different sets of params, how to write database queries in DAO layer. should i write separate methods each with a different query or one method with multiple if statements(like if user sent 'team' add team to DB query, if user sent 'score' add it to DB query ...)
Your second mapping explicitly specifies that both the team and score request parameters must be present, and they aren't in your example of /players?team=xyz. The fact that you wrote required = false on the binding for the method parameter is irrelevant--the #RequestMapping explicitly says that the parameter must be present in the request. (Note that you can have a parameter but no value, as in the common Spring Security URL of /login?error. This would match params = { "error" } but would have no value.)
It might be simpler to use a regular if statement in a single controller method.
Alternately, you may want to look into Spring's Querydsl integration, which allows you to interpret query parameters as a Predicate that can be passed directly to a Spring Data finder method.
Also, look into the new composite convenience annotations such as #GetMapping, which will make your code a bit simpler to read.
I have an entity object similar to the following
#Document(collection = "job")
public class Job {
#Id
private String id;
private JobStatus status;
#NotBlank
private String term;
...standard getters/setters
}
I have a controller something like this:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Job> createJob(#RequestBody #Valid Job job) {
...store the new job in the database
}
The Job.status property is set by the code before storing to the database. Any "status" property included in the request body to the createJob method is ignored.
I would like to validate that Job.status is not null when the job is written to the database, but users must not be required to include a status in the request body. If I annotate the Job.status property with #NotNull, the validation of the request body fails unless I include "status".
How do I validate the status field only when persisting to data store and not as part of the request body?
You're trying to use the database entity as a UI entity with different validation rules. You have options:
Use different classes for the UI and the DB
Create a custom JSR-303 validator for your not null which you somehow avoid applying when you validate in the UI layer
Put the constraint for not null in the database itself, rather than hibernate/JPA and handle the constraint exception
I have what appears to be a common problem within spring-mvc. Several of my domain object have fields that are not updatable so in my view I am not binding these fields.
For competeness sake The way these are excluded from the view is by editing the spring-roo scaffolded view setting the render attribute on the parameter to false.
As spring-mvc creates a new instance of the object rather than updating the existing object these fields are null. This means however that the object fails its validation before the control reaches the controller.
A lot of my entities will have extra fields that are not updatable in the view so I'd like to be able to come up with a generic solution rather than continually doing the same work over and over again (violating DRY).
How can one allow validation to occur in a consistent manner if fields are omitted from the view?
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String UserController.update(#Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, user);
return "admin/users/update";
}
uiModel.asMap().clear();
user.merge();
return "redirect:/admin/users/" + encodeUrlPathSegment(user.getId().toString(), httpServletRequest);
}
Possible Solutions:
Omit #Valid annotation from the controller.
Pros
Easy to implement.
Easy to understand.
Cons
Means changing the controller method for every update on every object.
Validation is not occuring in the same place as all of the rest of the application.
No easy way to return the binding errors back to the view (need to validate the object afterwards)
Add Custom Validator for methods that need omitted fields
Example:
#InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest request) {
if (request.getMethod().equals("PUT")) {
binder.setDisallowedFields("registrationDate", "password");
Validator validator = binder.getValidator();
Validator userUpdateValidator = new UserUpdateValidator();
binder.setValidator(userUpdateValidator);
}
}
Pros
Clear flow.
Cons
Suffers wildly from DRY problems. This means that If the domain object is altered in any way I need to revalidate.
Field validation is not the same as Hibernate validation when saving.
No tangible benefits over omitting validation and manually validating.
Would consider if?
Custom validator could delegate to standard JSR-303 validator but just omit fields.
Remove JSR-303 annotations from the domain object
Not an option this means that there is no validation on an object before saving. Worse I believe it will affect the DDL that is producted for database, removing constraints from the DB itself. Only put in here for completeness sake
Lookup domain object before validation occurs
The idea of this solution is to lookup the existing domain object before updating. Copying any not null fields to the old object from the request.
Pros
- The validation can go through the normal cycle.
- The validation doesn't need to change depending on what method you are implying.
Cons
Database access before hitting the controller has a bit of a smell.
I can't see any way to implement this.
Won't work for fields that need to be omitted during other stages of the object lifecycle. For example if adding a timestamp during creation.
I would like to know how to implement either a validator that delegates to the standard JSR-303 validator or alternatively how to lookup the object before modifying it. Or if anyone has any other possible solutions?
Either of these solutions allow for the treatment to be consistent over multiple objects.
Hopefully either would allow for added annotations such as.
#RooCreateOnly which means the domain object could be annotated as such leaving all the validation definitions in the one place.
The last option can be achieved with the #ModelAttribute annotation.
Create a method that returns your domain object and add the #ModelAttribute annotation to it. Then add the same annotation to the domain object argument of the method where you want to use that object. Spring will first load the object from the ModelAttribute method then merge it with the posted data.
Example:
#ModelAttribute("foobar")
public User fetchUser() {
return loadUser();
}
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(#ModelAttribute("foobar") #Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
return etc();
}
You can use the disabled property for the input tags in your jspx file containing the form for the fields that you want to mark as read-only.
Also make sure you clear the z attribute relating the field so that Roo will ignore the tag if there is any change made to the entity later on.
Cheers!
I'm posting another answer totally unrelated to my previous one.
There is another solution: wrap your domain object into special form object that only expose the fields you want to validate.
Example:
public class UserForm {
private final User user = new User();
// User has many fields, but here we only want lastName
#NotEmpty // Or whatever validation you want
public String getLastName() {
return this.user.getLastName();
}
public void setLastName(String lastName) {
this.user.setLastName(lastName);
}
public User getUser() {
return this.user;
}
}