I have a doubt about Model behavior in Spring MVC.
I have a controller class with to handler methods, say:
#RequestMapping(value = "/result", method = RequestMethod.GET)
public String getExportResults(#RequestParam("token") String token,
Model model) {
// ...
model.addAttribute("task", myObject);
// ...
}
#RequestMapping(value = "/file", method = RequestMethod.GET)
public void getFile(Model model, HttpServletResponse response)
// can't find "task" attribute...
}
When I put the "task" attribute into model, in my getExportResults I expect to find it into model argument of getFile method, but when I try to get it, "task" is null.
Am I wrong? Maybe model behaviour is not clear to me...
Your expectations are wrong. Putting something in the model makes it available for the current request only. The goal of adding something into the model is to make it available for the view, in order to generate the HTML page.
Model is initialized with every request, each request creates a new model object. The model you are adding your task object is not the same model object you get in getFile method.
If those are 2 different request, which seems like, you may want to put the task object into session and retrieve it from there.
Related
Background:
I am developing an web application with Spring MVC.
I want to make an aspect that is executed on POST requests and not executed on GET requests, because I want to inject the logic that prevent POST requests which are sent before completion of HTML rendering.
#RequestMapping(value = "/aaa", method = RequestMethod.POST)
public String methodForPost(AnDto dto, Model model) {
// the aspect should be executed on this method
}
#RequestMapping(value = "/bbb", method = RequestMethod.GET)
public String methodForGET(AnDto dto, Model model) {
// the aspect shouldn't be executed on this method
}
Question:
How can I specify method with an parameterized annotation and its value with #Pointcut ?
How can I specify method with an parameterized annotation and its value in <aop:pointcut> in Spring applicationContext.xml?
#Around(value="#annotation(RequestMapping)")
public Object display(ProceedingJoinPoint joinPoint, RequestMapping requestMapping ) throws Throwable {
// You have access to requestMapping. From which you can get whether its get or post and decide to proceed or not.
}
More info http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj-advice-params-passing
You might have to extend this to intercept only for Requestmapping's in your package. Because this intercepts every RequestMappig you might have in your project including one used by the libraries which you might be using, which is a burden.
I have been using the following code:
#RequestMapping(value="/myUrl", method=RequestMethod.GET)
public ModelAndView myRequestHandler(
HttpServletRequest request, HttpServletResponse response,
#ModelAttribute(value="paramName") #ValidMyModelForm form)
// automatically populates form setters from form:form in JSP view
{
}
Reading the answers at the following link I am starting to doubt that my usage of ModelAttribute is not correct here.
What is #ModelAttribute in Spring MVC?
Am I using it the right way? It seems to work but want to make sure I am not doing anything wrong.
The form object is added to model in a separate method using code that looks like:
modelAndView.addObject("formName", new MyModelForm());
In the JSP view I have a the forms name added as the commandName="formName".
This signature also should have worked perfectly for you:
public ModelAndView myRequestHandler(#Valid MyModelForm form)
here Spring MVC will ensure that a MyModelForm instance is created and bound based on what is submitted from your form.
Now, what does additional #ModelAttribute bring you:
First the simple case:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
Assuming you do not have any other method with #ModelAttribute, what the above will do is look for a model by name paramName, which is not likely to be present because of the assumption, then an instance of MyModelForm will be created and bound just like before, with one addition that you now have a model object with name paramName available that you can use in your view:
paramName.myformValue etc..
So in essence:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
is equivalent to:
public ModelAndView myRequestHandler(#Valid MyModelForm form, Model model) {
...
model.addAttribute("paramName", form)
}
Secondly, if you had a method annotated with #ModelAttribute which preloads say part of your MyModelForm:
#ModelAttribute("paramName");
public MyModelForm loadModel(int id) {
MyModelForm fromDB = loadFromDB(id);
}
Now, the advantage of your method signature:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
will be that the model that you have pre-populated from your DB, will be enhanced with what is submitted in the form.
I think the original question was confusing.
I have a HashMap that needs to be a Collection from a database that I'd like to send to a view via a Spring Controller. I don't want to put this HashMap in the model.addAttribute() because the Spring Model object returns a Map and my JSP needs the collection to be a Collection<Object>. If I set my HashMap.values() in a request.setAttribute, how do I go about dispatching that request variable to the view if my method is returning a String?
#RequestMapping(method = RequestMethod.GET)
public String home(Locale locale, Model model, HttpServletRequest request) {
model.addAttribute("surveys", mySurveys); //this is a map and I need a Collection<Object>
//So I'd like to do this, but how do I get to the "evaluations" object in a view if I'm not dispatching it (like below)??
request.setAttribute("evaluations", mySurveys);
//RequestDispatcher rd = request.getRequestDispatcher("pathToResource");
//rd.forward(request, response);
return "home";
}
EDIT: The Spring Tag library cannot be used for this particular usecase.
Thanks.
If mySurveys is a Map, then perhaps you can put mySurveys.values() into the ModelMap instead of mySurveys (Also, are you intending to use a ModelMap instead of a Model?)
In the code below, surveys would be a Collection of Objects and would be accessible in the jsp via ${surveys}
#RequestMapping(method = RequestMethod.GET)
public String home(ModelMap modelMap, HttpServletRequest request) {
Map<String,Object> mySurveys = getMySurveys();
modelMap.addAttribute("surveys", mySurveys.values());
return "home";
}
I think you're confused as to what ModelMap is.
You can annotate whatever variable you want to access in the view by #ModelAttribute and Spring will automatically instantiate it, and add it to the ModelMap. In the view, you can use it like:
<form:form modelattribute="myAttribute">
<form:input path="fieldInAttribute">
</form:form>
Hope this answers your question
I am using spring mvc with Annotations, see the following snippet
#RequestMapping(value = "/configuration/", method = RequestMethod.GET)
public MyModel viewConfiguration() {
The problem I am having accessing the 'MyModel' class in my JSP.
How can I do this, without using the ModelAndView object?
This shorthand syntax means that MyModel becomes a model attribute named myModel (i.e. class name with the first letter decapitalized).
View name is inferred from the URL.
See also:
15.3.2.3 Supported handler method arguments and return types
15.10.3 The View - RequestToViewNameTranslator
You could set MyModel as a request attribute to be accessed in your JSP. I'm curious, why don't you want to use ModelAndView? After all, it does what you want to do here, which displays the view and provides a container to hold your objects you want to reference in your view.
By the way, if this is an Ajax call, you will need to add #ResponseBody to the API so that your javascript can read the response in the callback function:-
#RequestMapping(value = "/configuration/", method = RequestMethod.GET)
public #ResponseBody MyModel viewConfiguration() {
...
}
This is the code example from Spring 3.1 Spring Source Blog: From XML to #Configuration I'm trying to implement in my application (which was done in Spring 2.0 not by me so it's lot of learning).
#FeatureConfiguration
class MvcFeatures {
#Feature
public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
.argumentResolvers(new CustomArgumentResolver());
}
// ...
}
However, I can't understand the point of .argumentResolvers(new CustomArgumentResolver()) and their CustomArgumentResolver looks like bellow. What's the point of it?
public class CustomArgumentResolver implements WebArgumentResolver {
#Override
public Object resolveArgument(MethodParameter param, NativeWebRequest request) throws Exception {
RequestAttribute attr = param.getParameterAnnotation(RequestAttribute.class);
if (attr != null) {
return request.getAttribute(attr.value(), WebRequest.SCOPE_REQUEST);
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
To add to #GaryF's answer, and to clarify some points, Spring 2.5 introduced annotated controllers, which replaced the old interface-style controllers of Spring 2.0. These new controllers have methods with no fixed parameters - the method declares the parameters that it needs to do its job, and nothing more.
For example, say a controller method needed one thing to do its job - a request parameter that contains the ID of an object from the database. In Spring 2.0, you would need to implement something like AbstractController.handleRequestInternal(), e.g
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
MyThing obj = getObjById(id);
//... do stuff and return ModelAndView
}
Spring 2.5 made that easier:
#RequestMapping
public ModelAndView handle(String id) {
MyThing obj = getObjById(id);
//... do stuff and return ModelAndView
}
Here, we only declare parameters for the stuff we need.
So far so good, but this is where a custom WebArgumentResolver comes in. Say I want to remove the getObjById from my controller altogether, because maybe I think it clutters up the code, and maybe it's used across many other controller methods. Instead, I want to do this:
#RequestMapping
public ModelAndView handle(MyThing id) {
//... do stuff and return ModelAndView
}
It's even simpler, and has a bare minimum of boilerplate code. A custom WebArgumentResolver can be registered with the app-context which recognises parameters of type MyThing, and knows how to extract the information from the request. Spring invokes that resolver, and passes the result to the controller method.
Custom resolvers aren't commonly used, but can be very handy in the right situation.
The example in your question uses CustomArgumentResolver to resolve the example's custom RequestAttribute class. The resolver pulls out request attributes and binds them to RequestAttribute objects, so that they can be declared as controller method parameters.
WebArgumentResolvers are a way for you to specify how the parameters of MVC-mapped methods should be resolved. If you'd like to use a custom object as a parameter for an MVC-mapped method, Spring tries to figure out how make sense of it in it's own way. Typically this would happen through binding, where some http parameters you submit match up with the fields of the object and Spring matches them up and creates a new object for you.
If you ever have a situation where the submitted parameters don't match up quite so neatly with your method parameters, WebArgumentResolvers are there to fill in the gap: you provide custom logic so Spring doesn't have to figure it out.
In your example, param is one such parameter to be matched up. This piece of custom code first checks if the parameter has an #RequestAttribute annotation. If it does, then the custom code pulls the value from that object and looks it up as an attribute on the http request, returning it. It it does not have that annotation, then the method returns the UNRESOLVED value, which simply indicates that this WebArgumentResolver doesn't know anything about this particular parameter and Spring should try a different method (such as binding).