RedirectAttributes giving IllegalStateException in Spring 3.1 - spring

I'm want to use RedirectAttibutes property that has come up in Spring 3.1, I have the following handler method for post in my controller
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(#ModelAttribute("admin") Admin admin, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes redirectAttributes) {
redirectAttributes.addAttribute("admin", admin);
if (bindingResult.hasErrors()) {
return REGISTRATION_VIEW;
}
sessionStatus.setComplete();
return "redirect:list";
}
But when I submit the form I'm getting the following exception:
java.lang.IllegalStateException: Argument [RedirectAttributes] is of type Model or Map but is not assignable from the actual model. You may need to switch newer MVC infrastructure classes to use this argument.
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:322)
I have come across a few gotcha's with redirectAttributes that you cannot use ModelAndView as the return type. So I returned just the string view.
Can anyone pl. tell me where I'm going wrong?
Thanks.

Spring 3.1 introduced new version of Spring MVC backend implementation (RequestMappingHandlerMapping/RequestMappingHandlerAdapter) to replace the old one (DefaultAnnotationHandlerMapping/AnnotationMethodHandlerAdapter).
Some new features of Spring MVC 3.1, such as RedirectAttributes, are only supported by the new implementation.
If you use <mvc:annotation-driven> or #EnableWebMvc to enable Spring MVC, new implementation should be enabled by default. However, if you declare HandlerMapping and/or HandlerAdapter manually or use the default ones, you need to switch to the new implementation explicitly (for example, by switching to <mvc:annotation-driven>, if it doesn't break your configuration).

Related

Spring #InitBinder on #RequestBody

I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.

Difference between #ModelAttribute and HttpServletRequest Attribute

I use spring mvc and I want to undestand some stuff.
At this code:
#RequestMapping(value="/enregistrerLostCard")
public #ResponseBody
void enregistrerLostCard(#ModelAttribute(value="auth") Auth auth2, HttpServletRequest request) {
Auth auth1 = (Auth) request.getAttribute("auth");
System.out.println(auth2.getLogin()); //print the right value
System.out.println(auth1.getLogin()); //i got nullpointer exception
}
#ModelAttribute(value="auth") and request.getAttribute("auth") isn't the same ?
HttpServletRequest is a Servlet container managed object. Its attribute store holds attributes that are useful in any part of the request handling by the Servlet container.
Model, ModelMap, ModelAndView, etc. are managed by Spring MVC (the DispatcherServlet stack). The attributes inside those are useful to the Spring side of the application.
In some cases, the Model attributes will be inserted into the HttpServletRequest attributes if needed. This typically happens when your handler method returns a String value as a view name. The model attributes will be pushed as HttpServletRequest attributes so that they can be used in the view, for example, in jsps.
Related:
how to pass variables from java to jsp in Spring

How to configure which controllers Spring #ControllerAdvice will be applied to?

I have two types of controllers in my spring application.
View controllers that forward to views to generate HTML
API controllers that return JSON directly from the controllers
Both the API and View controllers are part of the same spring dispatcher servlet. Spring 3.2 introduced the #ControllerAdvice annotation to allow for a global location to handle exception.
The documentation implies that #ControllerAdvice will be applied to every controller associated with a Dispatcher Servlet.
Is there a way to configure which controllers #ControllerAdvice will apply to?
For example in my scenario I want a #ControllerAdvice for my View Controllers and separate #ControllerAdvice for my API controllers.
For people that will still find this question:
As from Spring 4 ControlerAdvice's can be limited to Controler's with the specified annotations. Take a look at:
http://blog.codeleak.pl/2013/11/controlleradvice-improvements-in-spring.html
(second half of this article) for more details.
UPDATE
I am using spring 4. You can do one of 2 below options.
(1) You can add the packages you want. (Inside those packages you have controllers that you want to follow #ControllerAdvice).
Ex:
#ControllerAdvice(basePackages={"my.pkg.a", "my.pkg.b"})
(2) You can directly add the controller classes you want.
Ex:
#ControllerAdvice(basePackageClasses={MyControllerA.class, MyControllerB.class})
I do not think this is possible now. If you can make the API and View controllers throw different Exception types, then you could define two different #ExceptionHandlers and achieve what you want.
// For handling API Exceptions
#ExceptionHandler(APIException.class) // Single API Exception
#ExceptionHandler({APIException.class, ..., ,,,}) // Multiple API Exceptions
// For handling View Exceptions
#ExceptionHandler(ViewException.class) // Single View Exception
#ExceptionHandler({ViewException.class, ..., ...}) // Multiple View Exceptions
You could use aop to translate the Exceptions coming out of APIs to a standard APIException. See this thread on spring forums.
Hope it helps.
Your exceptions should not dictate the content-type of your response. Instead check the request's Accept header for what the browser expects.
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {
...
String header = request.getHeader("Accept");
if(supportsJsonResponse(header)) {
//return response as JSON
response.setContentType(JSON_MEDIA_TYPE.toString());
return Json.stringify(responseMap);
} else {
//return as HTML
response.setContentType("text/html");
}
#ExceptionHandler(value=Exception.class)
public ModelAndView error(Exception ex) {
return new ModelAndView("redirect:/error/m");
}
...//ErrorController
#RequestMapping(value = "/m", produces="text/html")
public ModelAndView error()...
#RequestMapping(value = "/m", produces="application/json")
#ResponseBody
public Map errorJson()...

Spring MVC Annotation Based Controller command object losing values on POST

I am loading a user object my calling a service and then store this user as a command object in the model on GET in the controller. This user object has many properties that are not mapped in the jsp page. After submitting the form, I am getting the command object i the controller on POST. But strangely, I only see the properties in the command object which are mapped to the jsp page. All the other properties those were there when I load the object are lost. I need all the properties in object to be able to successfully save it in hte database.
Can anybody help me figure this problem? Thanks!
Update
I am adding some code to better understand it. In POST handler, I was expecting the command object to have all the properties that was loaded in GET handler in addition to the properties that are bound with jsp. Instead I am losing all propeties except those are bound to the jsp. Am I doing something wrong here?
#RequestMapping(method = RequestMethod.GET)
public String showForm(ModelMap model, HttpSession session, HttpServletRequest request) throws Exception {
UserBean user = Util.getUser(session);
UserBean command = (UserBean)userProfileService.loadByUserName(user.getUserName());
model.addAttribute("command", command);
return formView;
}
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("command") UserBean command, BindingResult result, HttpSession session) throws Exception {
UserBean user = (UserBean) command;
userProfileService.saveUser(user);
return "successView";
}
Update
I am adding some code to better understand it. In POST handler, I was expecting the command object to have all the properties that was loaded in GET handler in addition to the properties that are bound with jsp. Instead I am losing all propeties except those are bound to the jsp. Am I doing something wrong here?
#RequestMapping(method = RequestMethod.GET) public String showForm(ModelMap model, HttpSession session, HttpServletRequest request) throws Exception { UserBean user = Util.getUser(session); UserBean command = (UserBean)userProfileService.loadByUserName(user.getUserName()); model.addAttribute("command", command); return formView; }
#RequestMapping(method = RequestMethod.POST) public String onSubmit(#ModelAttribute("command") UserBean command, BindingResult result, HttpSession session) throws Exception { UserBean user = (UserBean) command; userProfileService.saveUser(user); return "successView"; }
Update
If I store the command object in session how would the jsp bind the propeties. I thought I needed to store it in model for that?
Could you explain please.
Update
storing the command object in session solves the problem. I was able to store it by using
#SessionAttributes ("command")
Thanks a lot!
That's expected behaviour. Spring does not take your existing object (how would it get it?) - it creates a new one and fills it with data.
You can use the #ModelAttribute annotated-method to specify a method which will load the existing object from storage (db?) by its ID (submitted).
#ModelAttribute annotated methods are executed before the chosen #RequestMapping annotated handler method. They effectively pre-populate the implicit model with specific attributes, often loaded from a database. Such an attribute can then already be accessed through #ModelAttribute annotated handler method parameters in the chosen handler method, potentially with binding and validation applied to it.
See 15.3.2.8 of the MVC docs
The Spring-MVC Model values become Request scope attributes in your JSP. This is a one way translation, Spring-MVC does not restore the Model values when the user POSTs a form from your page.
When I need to store information between a GET and a POST (that is, set something on a GET and read it back on a POST), I set a Session attribute.
In your case, I believe that you will need to do one of the following:
Call Util.getUser(session); in the onSubmit method.
Store the command object in the session in the showForm and retrieve it in the onSubmit method>
#ModelAttribute is used to direclty set the values in the Student object from the jsp, other wise in the servlet you have to get the properties using request.getattribute() and than call student setter method.
so u can use both the keywords in jsp page.
<form action="grade" method="get" name="contact1" modelAttribute="contact1">
</form>
or

Custom bean instantiation logic in Spring MVC

I have a Spring MVC application trying to use a rich domain model, with the following mapping in the Controller class:
#RequestMapping(value = "/entity", method = RequestMethod.POST)
public String create(#Valid Entity entity, BindingResult result, ModelMap modelMap) {
if (entity== null) throw new IllegalArgumentException("An entity is required");
if (result.hasErrors()) {
modelMap.addAttribute("entity", entity);
return "entity/create";
}
entity.persist();
return "redirect:/entity/" + entity.getId();
}
Before this method gets executed, Spring uses BeanUtils to instantiate a new Entity and populate its fields. It uses this:
...
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
Here's the problem:
My entities are Spring managed beans. The reason for this is to inject DAOs on them. Instead of calling new, I use EntityFactory.createEntity(). When they're retrieved from the database, I have an interceptor that overrides the
public Object instantiate(String entityName, EntityMode entityMode, Serializable id)
method and hooks the factories into that.
So the last piece of the puzzle missing here is how to force Spring to use the factory rather than its own BeanUtils reflective approach? Any suggestions for a clean solution?
Thanks very much in advance.
You can use #ModelAttribute-annotated method to pre-populate the model with your bean. Then data binder will use that bean instead of instantiating the new one. However, this will affect all method of the controller.
#ModelAttribute
public Entity createEntity() { ... }

Resources