databinding,validate,exception in spring mvc3 - spring

I am using the spring mvc3,and I found that I am confused with the some concepts,so I make a summary, and post it here.
Data binding.
I use struct2 before where if I have a bean in the action(i..e,named 'Person'),then when the following url is requested:
http://localhost:8080/app/form?person.id=1&person.name=xxx&pet.name=yy
Then a new instance of Person will be created and the parameters '1' and 'xxx' will be populated to this instance.
And a new instance of Pet will be created,and the name 'yy' will be populated to this bean.
This is the only way for data binding in struct2:
You specify the beanname.property in your request.
Now in spring mvc3,I know one way to binding the data:
http://localhost:8080/app/form?id=1&name=xxx&other=yy
#RequestMapping('form')
public String form(Person person,BindingResult result){
//now I get the person
}
Even it work,but I have some questions:
1) I have three parameters in the request url "name",'pass','other',how does spring know which parameters should be populated to my bean(model).
2) how about if I want to bind parameters to more than one model like the example in struct2(both person and pet)?
Furthermore,what does the modelAttribute and commandName attribute in the spring tag "form" mean?
2. Validate
For bean(ignore the getter and setter in the example):
import javax.validation.constraints.NotNull;
public class Bean {
#NotNull(message="id can not be null")
private int id;
}
I use this controller:
#RequestMapping("val")
public #ResponseBody
String validate(#Valid Bean bean, BindingResult result) {
if (result.hasErrors()) {
return result.getAllErrors() + "";
} else
return "no error";
}
I found that only the 'NotEmpty' works.
Since if I use this url:
/val?name=xxx ==> no error !! // id is null now,why it does not has error?
3. The type conversion.
Take this url for example:
http://localhost:8080/app/form?id=1&name=xxx&other=yy
Obviously it will throw 'NumberFormatException' if I set the id to string value.
Then where to catch this exception? If in the controller,then I have to write so many try-catch block in each controller.
Furthermore,the the type conversion exception do not limited to only 'NumberFormatException',it may be 'dataformaterrorexception' and etc.
But they all belongs to the type conversion,how to handle the using a common solution?

Related

Bind json value to two object in spring MVC restful

At present what I have is one view in HTML for entering Person's details and Company's details. I am using spring MVC framework restful.
I create json and send request using Ajax to Restcontroller.based on URL pattern create method is called .e.g. json is
{"name":"rohit","address":"Pune","company":"ABC"}
Here above name and address belong to person bean and company belongs to company bean. I want the json value bind to their respective bean. How to do it? I have tried the code below but I know it won't work.
#Requestmapping(value="/createperson",method=method.post)
public #Responsebody String createperson(#Requestbody person,#Requestbody company)
{
//Some code to save
}
I have a form, which will input the person's details and the person's company details.
What I want is that when this form is submitted, some of its fields are bound to Person object properties and some to Company object properties. How can this be done? And how to do validation for json value and send all errors as json responsive again back if there are any errors.
You can only have one #RequestBody. Spring then looks at the content-type header and finds an appropriate HttpMessageConverter which will read the entire http entity body (input stream) into a single object.
What you have basically done is try to merge Person and company into a single JSON object, and thereby flattened the structure. If you want spring to handle that, you need to create a new object with the same (flat) hierarchy. Or you need to create a wrapper class PersonAndCompany which contains both a Person and a Company, and then change the JSON to match the structure, so it looks like this.
{
"person" : {
"name":"rohit",
"address":"Pune"
},
"company" : {
"name":"ABC"
}
}
you should do like this if you are using relationship between Person and Company otherwise it is better to use single bean instead of two.
#ResponseBody
#RequestMapping(value = "/createperson", method=RequestMethod.POST ,consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Person> createperson(#RequestBody Person person) {
if(error found ){
Person p new Person();
p.setError(" error message ");
return new ResponseEntity<Person>(p,HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Person>(person,HttpStatus.OK);
}
public class Person {
private String name;
private String address;
Company company;
String error;
--- setters getters
}
public class Company {
String compName;
--- setters getters
}
input json
{"name":"person name ","address":"person address ","company":{"compName":"company name"}}

How to get values to ArrayList of Class, from jsp page using jstl tag?

This is my first VO class member variable:
private List<ProgramToStudyMap> programToStudyMaps;
And here is ProgramToStudyMap Class:
public class ProgramToStudyMap {
private Long studyId;
private List<Long> programId;
}
And my jsp code is,
<form:checkbox path="programToStudyMaps[0].studyId" />
But it's giving error like,
org.springframework.beans.NotReadablePropertyException: Invalid property 'programToStudyMaps[0]' of bean class [java.util.ArrayList]: Bean property 'programToStudyMaps[0]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
This is my controller:
#RequestMapping(value = RequestMap.MAINTENANCE_STUDY_PROGRAMS, method = RequestMethod.POST)
public #ResponseBody JsonResponse maintananceStudyProgramsSave(#ModelAttribute ProgramToStudyVo programToStudyVo) {
JsonResponse jsonResponse = new JsonResponse();
try {
System.out.println("---------- "+programToStudyVo.getProgramToStudyMaps().get(0).getStudyId());
System.out.println("---------- "+programToStudyVo.getProgramToStudyMaps().get(0).getProgramId());
System.out.println("------------- "+programToStudyVo.getProgramToStudyMaps().get(1).getStudyId());
System.out.println("------------- "+programToStudyVo.getProgramToStudyMaps().get(1).getProgramId());
jsonResponse.setStatus(RhoConstants.JSON_SUCCESS);
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
}
return jsonResponse;
}
How can I resolve this.?
The problem is you are sending the json response from the server and trying to display the results using spring:form tags , which cant be done as though.
You can do a alternative method if you want to end up with json, handle the response using javascriptand populate the checkbox values
Else if you wish to go with spring:form you need to add the values to the Model using model.addAttribute() method and display it using path property
Also remember path property should match the property names of your model class.
IMHO , take this tutorial to have some idea before you start with.

Spring Mvc ModelAttribute with referencing name is not working?

I want to to create different #Entity entities within the same Controller.
#RequestMapping(value="create", method=RequestMethod.GET)
public String GET(Model model) throws InstantiationException, IllegalAccessException
{
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
model.addAttribute("object", object);
return "create";
}
#RequestMapping(value="create", method=RequestMethod.POST)
public #ResponseBody Object POST(#ModelAttribute(value="object") Object object)
{
System.out.println("POST! got type: " + object.getClass().getName());
return object;
}
In the Post Method I get NULL for #ModelAttribute(value="object") Object object
If I change it to #ModelAttribute(value="object") realType object it is working perfectly fine. But I don't know the type yet.
I thought the #ModelAttribute can achieve this anyway with the name "object" but apparently not. What am I missing?
There is no actual model object named object when you submit, spring constructs it based on the parameter type and will bind the properties accordingly.
You have 2 choices to make it work
Store the object in the session
Use a #ModelAttribute annotated method
If neither of these are there spring will simply look at the method argument and use reflection to construct an instance of that class. So in your case it will only be Object and after that binding will fail.
Store object in the session
#Controller
#SessionAttributes("object")
public class MyController { ... }
Make sure that when you are finished that you call the setComplete() method on a SessionStatus object.
Use a #ModelAttribute annotated method
Instead of creating and adding the object in a request handling method create a speficic method for it.
#ModelAttribute("object")
public Object formBackingObject() {
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
return object;
}
This method will be called before each request handling method, so that a newly fresh object is constructed which will be used for binding.

spring security : Why can't we access Hibernate entitiy parameters in #PreAuthorize?

I have the following interface method on which I am applying #PreAuthorize :
#PreAuthorize("doSomething(#user.id)")
void something(User user, List<User> accessList);
where User is a Hibernate entity object. It gives me an error :
org.springframework.expression.spel.SpelEvaluationException:
EL1007E:(pos 13): Field or property 'id' cannot be found on null at
org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:204)
There is no way that the user parameter is null, as if I remove the annotation, and inspect the value of user in the method that implements this interface method, there is a valid User object present there. Additionally, just before calling this method, I have made sure that the user object is correctly constructed.
I really can't figure out why would the user field be considered null by the SPEL parser
You can check with the debugger what's going on in MethodSecurityEvaluationContext, inside Object lookupVariable(String name) method:
#Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!argumentsAdded) {
addArgumentsAsVariables();
argumentsAdded = true;
}
and so you can see what's really going on in the addArgumentsAsVariables() method as the convertion of method arguments to SPEL variables is implemented very clearly in Spring.
Spring Security has a better answer for this problem now:
http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#access-control-using-preauthorize-and-postauthorize
Basically, you can use the #P annotation or #Param annotation if you are using < JDK 8.
You can check LazyParamAwareEvaluationContext,inside loadArgsAsVariables() method, version 3.1.0.
The same key for different Entity, because of implementing interface.
I need to add something to this as the title indicates that we cannot access hibernate properties.
There are two editions of hasPermission, the loaded object and the serialized object. Here is some code from a test case:
#PreAuthorize("isAuthenticated() and hasPermission(#organization, 'edit')")
public long protectedMethod(Organization organization)
{
return organization.getId();
}
And for the latter here we see that we can infact access the id proprty of the organization (which is a hibernate entity):
#PreAuthorize("isAuthenticated() and hasPermission(#organization.getId(), 'organization', 'edit')")
public long protectedMethodSerializableEdtion(Organization organization)
{
return organization.getId();
}

Spring MVC 3.0: How do I bind to a persistent object

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.

Resources