Is there a way to stop Spring from adding in reference data from methods marked with #ModelAttribute into the URL on redirects? - spring-mvc

I'm currently using the #ModelAttribute annotation in my controllers to add reference data to my pages and forms, ie:
#ModelAttribute("someValue")
public String getSomeValue() {
return someValue;
}
This works great until I start using redirects from the controllers. All of the values from methods marked with #ModelAttribute appear in the URL, ie:
http://somedomain.com/page?someValue=value
Is there a setting to turn this off? Or is there a simple fix for this?
I read something about creating an interceptor for adding reference data into a model, but that just seems wrong:
http://developingdeveloper.wordpress.com/2008/02/28/common-reference-data-in-spring-mvc/

I found out that there is a setter on the RedirectView object called setExposeModelAttributes. If you set it to false, the attributes don't get thrown into the URL.
I got some help from PUK_999 in the spring source forums:
http://forum.springsource.org/showpost.php?p=274948&postcount=6

This is intentional and specific behaviour of #ModelAttribute, even if it does feel wrong and broken.
An interceptor is really one of the easiest ways of doing this.

Related

Spring MVC 3.1 - Model Attribute lost

I have a quick question on scope of ModelAttributes.
Dev. Env: Spring MVC 3.1/Java 6/JSP w/JSTL for Views
In my controller, I add an attribute to the model via
model.addAttribute(“appForResubmission”, appForResubmission);
In the JSP(served out in response to a GET request) I read it’s contents as:
${appForResubmission.appId}
— works fine and the data is shown on JSP as expected.
Upon submission of the JSP, in the same controller in a different method(in response to a PUT request), I try to read the attributes from the Model for any changes and I am doing this as
#ModelAttribute(“appForResubmission”) Application app
in the method signature.
However, all I get is a new Application object when I try to interrogate the object for data. Spring’s documentation says this kind of instantiation of a new object happens when the requested attribute does not exist in the Model.
What would cause the attribute to be lost? Any ideas? I am suspecting it is a scope issue someplace but I am not sure where the problem could be.
Any pointers you could provide is greatly appreciated?
Thank you,
M. Reddy
The scope of a modelattribute is the request, internally it is just equivalent to HttpSerletRequest.setAttribute("model", model).
If you want the model to be available in a different controller you probably have two options, one is to reconstruct it, based on what you submit to the controller or using your persistent source. The second option is for specific model attributes to be added to the session using #SessionAttribute({'modelname'}), but just be careful that you have to call SessionStatus.complete to remove the model added to the session later.

Manually doing what the ModelAttribute annotation does in Spring MVC

I have an action method on a Spring MVC controller that has an argument annotated with #ModelAttribute. However, I don't know at compile time what the type of this parameter will be - I know the abstract base type but not the derived type.
At runtime, I will be able to decide what class I am expecting and I will be able to get a new'd up instance of this class. However, I have no idea what code I should be calling to parse the request data in the same fashion that #ModelAttribute does.
I've looked around and it seems that if i can get a hold of a WebRequestDataBinder I can use that to populate my object, but for that I need a BinderFactory and this is where I kind of get lost.
Can anyone give me some pointers here - or tell me that I am looking at it the wrong way and need to do something else?
you can inject the model itself in your controllers method and access the attribute yourself.
#RequestMapping(...)
public void doStuff(ModelMap model) {
Object attr = model.get("nameOfAttribute");
// ...
}

Does Velocity template has implicit request object?

I'm rephrasing my existing question to a more generic one. I want to know if Velocity has got implicit object references like JSP does.
I'm particularly interested in knowing about the request object.
In JSP we can get the attribute in the request scope like <%= request.getAttribute("req1") %>
I know that JSP is a servlet and <%= request.getAttribute("req1") %> ends up as a part of _jspService() method which has the request object available to it before the scope of the request ends.
I'm not sure how Velocity works behind the scenes (it may be leaving the request object behind by the time it plays it role)
To test that I did the following thing which was a the part of my previous question.
I have a Spring MVC TestController in which I'm setting a request attribute. I'm using Velocity templates for rendering the views.
#RequestMapping(value="/test", method=RequestMethod.GET)
public ModelAndView display(HttpServletRequest req, HttpServletResponse resp){
...
req.setAttribute("req1", "This should be present for first request");
...
}
In the Velocity template I'm doing something like
Request: $request.getAttribute('req1')
but I'm not getting the value of req1. I know I should have put req1 in model map instead of request but I want to know about implicit request object ref.
I tried $req1 as well but its not working.
When I'm doing the same thing with the model and returning it back, everything is working correctly.
Where am I going wrong?
Update: The same thing is happening with req.getSession().setAttribute("req1", testObject) also.
Salaam,
req.getSession().getAttribute("req1", testObject) == $req1
AFAIK, you cannot access the request object at VelocityViewServlet's templates, unless you explicity set the request object in context or use a v-tool .
Take a look at this question: Velocity + Spring. The Spring folks haven't kept the integration with Velocity very up to date.
Once you've created that extension and set it up to be used properly in your servlet configuration, you'd be able to simply put the object on the ModelAndView and from there do whatever you need with it.

Spring MVC - pass model between controllers

I have created a controller that does some business logic and creates a model. If I pass this model directly to view by returning ModelAndView with view name and model - everything working great. But now I want to display results at another page. So I use "redirect:" prefix to redirect to another controller, but the model is lost.
What Im missing?
Regards,
Oleksandr
you can use the forward: prefix which ultimately does a RequestDispatcher.forward()
insted of The redirect: prefix.
Option 1 :
You might put the model in session and get it back in the controller and nullify it in session.
Option 2 :
You said, you are having two controllers, first one would retrieve the user input and do some business logic and redirect to other one. My suggestion is to move the business logic which is placed in both controllers to a class and have only one controller which would return the model and view to the user.
During redirect: request is sent using GET method with parameters appended to the url. You can right a new method in the controller to receive the request parameters with #RequestParameter annotation.
While redirecting the request simple add all the parameters as string
e.g ModelAndView mv = new ModelAndView();
mv.setViewName(redirect:/your/url);
mv.addObject("param1", string_param);
.
.
.
This way you can redirect successfully.
Since the redirect: prefix runs another controller, you will lose whatever you had in the ModelAndView in the previous controller. Perhaps the business logic controller should not be a controller at all -- maybe you should just make it a regular class, and have the results controller call it. Then the results controller could save the data to the model.
i would not use #SessionAttributes as it may change in future releases of spring. I would stick with the old fashioned way that Oleksandr showed you above
somehow an old post but maybe someone else will find it usefull

In asp.net mvc is it possible to make a generic controller?

I'm attempting to create a generic controller, ie:
public class MyController<T> : Controller where T : SomeType
{ ... }
However, when I try to use it, I'm running into this error everywhere...
Controller name must end in 'Controller'
So, my question, Is it possible to make a generic controller in asp.net mvc?
Thanks!
If I understand you properly, what you are trying to do, is route all requests for a given Model through a generic controller of type T.
You would like the T to vary based on the Model requested.
You would like /Product/Index to trigger MyController<Product>.Index()
This can be accomplished by writing your own IControllerFactory and implementing the CreateController method like this:
public IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = Type.GetType("MyController")
.MakeGenericType(Type.GetType(controllerName));
return Activator.CreateInstance(controllerType) as IController;
}
Yes you can, it's fine and I've used them lots myself.
What you need to ensure is that when you inherit from MyController you still end the type name with controller:
public class FooController : MyController<Foo>
{
...
}
The default controller factory uses "convention" around controller names when it's trying to find a controller to dispatch the request to. You could override this lookup functionality if you wanted, which could then allow your generic controller to work.
This MSDN article...
http://msdn.microsoft.com/en-us/magazine/dd695917.aspx
... has a good writeup of what's going on.
This is a duplicate of asp.net mvc generic controller which actually contains the correct answer. Jeff Fritz's answer is absolutely not correct. Creating your own IControllerFactory will not get past the limitation in ExpressionHelper.GetRouteValuesFromExpression which is generating the error you are seeing. Implementing your own IControllerFactory will still leave you with errors whenever you call RedirectToAction, BuildUrlFromExpression, ActionLink, RenderAction, BeginForm, any any methods that call those.
What is interesting to me, is that Microsoft's "restriction by convention" is already enforced by the constraint "where TController : Controller" that is placed upon the type in the ExpressionHelper.GetRouteValuesFromExpression method. No generic will ever satisfy the convention validation:
string controllerName = typeof(TController).Name;
if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) {
throw new ArgumentException(MvcResources.ExpressionHelper_TargetMustEndInController, "action");
}
unless it is inherited by a class ending in "Controller" because typeof(AnyGeneric).Name will never end with "Controller".
If i was you, i'd get the MVC source and create a test MVC project with the source code so you can examine where the exception is generated and see what you can do about your generic idea and the enforced "*controller" naming convention.

Resources