How model annotated methods should interact? - spring

I would like to know how controller methods should interact with ModelAttribute annotated methods.
For example handlePage method would like to filter the list created by createList method?
Or set the id for the object created by createAnObject method?
Is it possible or ModelAttribute annotated methods are designed to attach static data to the model?
#ModelAttribute("someList")
public ArrayList<SomeList> createList() {
return new ArrayList<SomeList>(100);
}
#ModelAttribute("anObject")
public AnObject createAnObject() {
return new MyObject();
}
#RequestMapping(method=RequestMethod.GET)
public void handlePage(Model model) {
//Do some stuff to populate the model....
}

The two shouldn't really interact. #ModelAttribute, in this context, is intended for exposure of reference data, i.e. data that doesn't depend on the details of the request.
If your handler method needs to modify that data, then #ModelAttribute isn't appropriate. Instead, the handler method should explicitly add the data to the model after modifying it.

Related

How to do Spring Controller method specific serialization with Jackson?

I have two different serializers for String fields. I want to use either of them conditionally based on an annotation present on the calling Controller method. I'm looking at different ways of doing this via Jackson (eg. annotationIntrospector, JsonView etc). However, I do not see anywhere I can use method annotation during serialization. I can probably check if I can follow something similar to how Jackson implements JsonViews but haven't got to a solution yet.
Here is the use case.
// Dto
public class MyDto {
#Masked //Mask the fields with an option to avoid masking based controller method annotation.
private final String stringField;
// getters, setters.
}
// controller.
// default behavior is to serialize masked.
#ResponseBody
public MyDto getMaskedDto() {
// return dto with masked value.
return this.someService.getDto();
}
// Controller
#IgnoreMasking // Do not mask the dto if method is annotated with #IgnoreMasking.
#ResponseBody
public MyDto getDtoSkipMasking() {
// return dto without masking String field value.
return this.someService.getDto();
}
You could extend Jackon's StdSerializer and override the serialize method.
So something like this:
Create a new CustomSerializer class extending StdSerializer
Override the serialize method
In the overridden method, check for the existence of the object being serialised for the existence of your custom annotation (ie IgnoreMasking). You can do this via reflection
Do your processing
Register your custom serializer into Jackson's ObjectMapper configuration as a new SimpleModule

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.

Add a Model Attribute using ControllerAdvice

I have marked a class with #ControllerAdvice
Added a method
#ModelAttribute
public void setSourceAppId(Model model)
{
model.addAttribute("myattribute","1234");
}
But this method is not getting called at all.
I have not yet used modelattribute with a setter method so i cannot tell that thats a wrong approach, but changing it to a getter will do the job. Also, you dont need to add it to the model yourself. Modelattribute is handling exactly that.
#ModelAttribute("myattribute")
public int getSourceAppId()
{
return 1234;
}

AOP for Spring Controllers

Spring's AOP functionality is pretty great, and it makes it easy to add cool and useful annotations to controllers. For example, I wrote an #Authenticated annotation that either allows authenticated users through to the controller method or redirects to the login page. Fun stuff.
However, Spring's controllers can return all sorts of different types. They can return Strings, ModelAndView objects, or even void. There are methods in my code base that use all three types. However, I'd like to change my #Authenticated annotation to render and return a particular page, which I was hoping to do by returning a ModelAndView object. Is the only way to accomplish this by requiring all of my controller methods to return a ModelAndView?
Example of a controller I'd like to have:
#Controller
public class MyController() {
#Authenticated
#RequestMapping("/myscore")
public String myScorePage(ModelMap model) {
return "myScorePage";
}
#Authenticated
#RequestMapping("/anotherPage")
public ModelAndView something() {
return new ModelAndView("anotherPage",someModelStuff());
}
}
#Aspect
public class NotVeryUsefulAspect {
#Around("#annotation(Authenticate)")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
if( isAuthenticated() ) {
return pjp.proceed();
} else {
return /* Oh no what goes here, I want to render a FAILURE page without redirecting */
}
}
}
Ha, figured it out!
I decided to use the ProceedingJoinPoint passed to the aspect method to figure out the return type of the original method. Then I made a set of possible "failure" results for the aspect method based on what type of return is passed. For example, if the method originally returned a String, I return "failure_page", and if the method returned a ModelAndView, I return a new ModelAndView("failure_page").
Works quite well! Unfortunately, I may not have an opportunity to set a model object if it returns a string and doesn't take a ModelMap as a parameter, but I can deal with that for an error page just fine.
Yes it seams that you are right.
You need to change your methods so that all return an ModelAndView.
Or you need two Aspects, one for return type ModelAndView and one for String - and then all your controller methods must match
But Authorization is already build in in Spring Security and you do not need to implement it by your own.

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