Am I using #ModelAttribute wrong in my Controller? - spring

For years I have been using #ModelAttribute to create and initialize my command object like so:
#RequestMapping()
public String someHandler(#ModelAttribute("formBean") FormBean formBean) {
// Do something
}
#ModelAttribute("formBean")
public FormBean createFormBean() {
FormBean formBean = new FormBean();
// Do some sort of initialization
return formBean;
}
In this example, I have a handler in a Controller that needs a FormBean, and a "create" method that gives it one if one isn't already in the Model (or session, if using #SessionAttributes). So, when my someHandler() method is ran, the FormBean is already there and populated because my createFormBean() had already ran.
However, my colleague is claiming that, although this works just fine, that I am misusing the #ModelAttribute for a purpose it wasn't intended for, namely in the creation of the Command object. In his interpretation from the JavaDoc, you should only use #ModelAttribute to create static data, like items used to populate a dropdown list or such.
I know this works for creating and initializing my Command object quite well, but am I using this for a purpose it was not originally intended for? Am I breaking some cardinal rule here?

#ModelAttribute("formBean")
public FormBean createFormBean() {
FormBean formBean = new FormBean();
// Do some sort of initialization
return formBean;
}
This can be useful if you need to initialize model attribute before binding form values from view. For example, you can query object from database (to get available it in current session).
In other cases I prefer to use this method:
#RequestMapping
public String someHandler(final Model model) {
FormBean formBean = new FormBean();
// Do some sort of initialization
model.addAttribute("formBean", formBean);
}
I think it more clear to understand. But I don't think that you "breaking some cardinal rule here".

Related

How to correctly initialize an object that have to contain the data retrieved by 2 methods of my controller in this Spring MVC application?

I am pretty new in Spring MVC and I have the following doubt about how correctly achieve the following task.
I am working on a web application that implement a user registration process. This registration process is divided into some consecutive steps.
For example in the first step the user have to insert a identification code (it is a code that identify uniquely a user on some statal administration systems) and in the second step it have to compile a form for his personal data (name, surname, birth date, and so on).
So, actually I have the following controller class that handle these steps:
#Controller
public class RegistrazioneController {
#Autowired
private LoadPlacesService loadPlacesService;
#RequestMapping(value = "/iscrizioneStep1")
public String iscrizioneStep1(Model model) {
return "iscrizioneStep1";
}
#RequestMapping(value = "/iscrizioneStep2", method=RequestMethod.POST)
public String iscrizioneStep2(Model model, HttpServletRequest request, #RequestParam("cf") String codicFiscale) {
System.out.println("INTO iscrizioneStep2()");
//String codicFiscale = request.getParameter("cf");
System.out.println("CODICE FISCALE: " + codicFiscale);
model.addAttribute("codicFiscale", codicFiscale);
return "iscrizioneStep2";
}
#RequestMapping(value = "/iscrizioneStep3", method=RequestMethod.POST)
public String iscrizioneStep3(#ModelAttribute("SpringWeb")Step2FormCommand step2Form, ModelMap model, HttpServletRequest request) {
System.out.println("INTO iscrizioneStep3()");
System.out.println("NOME: " + step2FormCommand.getName());
return "iscrizioneStep3";
}
Into the iscrizioneStep2() it is retrieved the first code (#RequestParam("cf") String codicFiscale).
Into the iscrizioneStep3() it is retrieved a command object containing the data inserted into the form of the view in which this form was submitted, this one:
#ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand
It works fine.
Now my problem is that I have another object named Step3View that have to be initialized with the aggregation of the #RequestParam("cf") String codicFiscale object retrieved into the iscrizioneStep2() method and the #ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand retrieved into the iscrizioneStep3() method.
This Step3View class simply contain the String codicFiscale and all the fields of the Step2FormCommand class.
Now my doubts are: what is the best way to handle this situation? Where have I to declare this Step3View object? at controller level? (so I can use it in all my controller methods?). Have I to annotate this class with #Component (or something like this) to inject it in my controller?
What is the best solution for this situation?
I think in order to get an answer you need to understand the question and ask the right question. I think your question is "how do I pass a parameter from one page to another page in SpringMVC?". You specifically want to know how to pass the "cf" param, but readers here will tend to pass over questions that are too specific because it takes too much time to figure out what you want.
In answer to that, see Spring MVC - passing variables from one page to anther as a possible help.
Also, there are many good answers about this question for JSP in general, which can be worked into the SpringMVC architecture. See How to pass value from one jsp to another jsp page? as a possible help.

Spring MVC - difference between HttpSession.setAttribute and model.addObject

I am trying to learn Spring MVC recently. It seems that i did not understand well the functionalities of #ModelAttribute annotation and HttpSession.
#SessionAttributes({"shoppingCart", "count"})
public class ItemController {
#ModelAttribute("shoppingCart")
public List<Item> createShoppingCart() {
return new ArrayList<Item>();
}
#ModelAttribute("count")
public Integer createCount() {
return 0;
}
#RequestMapping(value="/addToCart/{itemId}", method=RequestMethod.GET)
public ModelAndView addToCart(#PathVariable("itemId") Item item,
#ModelAttribute("shoppingCart") List<Item> shoppingCart, #ModelAttribute("count") Integer count) {
if(item != null) {
shoppingCart.add(item);
count = count + 1;
}
return new ModelAndView(new RedirectView("showAllItems")).addObject("count", count);
}
#RequestMapping(value="/deleteFromCart/{itemId}", method=RequestMethod.GET)
public ModelAndView deleteFromCart(#PathVariable("itemId") Item item,
HttpSession session) {
List<Item> list = (List<Item>) session.getAttribute("shoppingCart");
list.remove(item);
//session.setAttribute("shoppingCart", list);
Integer count = (Integer) session.getAttribute("count");
count = count - 1;
session.setAttribute("count", count);
return new ModelAndView(new RedirectView("showAllItems"));
}
ShoppingCart and count are the session attributes.
The problem is in the deleteFromCart method. I get the count from session, reassign it and overwrite it in session. But i cant see the updated value of count on jsp. However, the updated shoppingCart object can be seen updated, although i do not overwrite the session object (since the object is the same object which is already in session).
But why is the count not updated, although i overwrite it with session.setAttribute?
When i add the new count object to the model (model.addObject("count", count)) then i can see the updated value of count. But why does not session.setAttribute give the same result?
First of all, #SessionAttribute does not have to use the http session. It uses a SessionAttributeStore which can have anything as its backing storage. Only the default implementation uses the http session.
The reason why your code does not work as expected lies in how #SessionAttribute works.
Before a controller method is invoked, everything listed in #SessionAttributes, in your case {"warenkorb", "count"}, is read from the session and added to the model.
After the method has returned the session is updated with everything that has been added to the model within the method.
.addObject("count", count)
-> count is added to the model and afterwards to the session.
session.setAttribute("count", count)
-> count is added to the session but not to the model. It will be added to the model before the next invocation of any controller method. But as for now the model still has the old count. And the model is what gets added to the request. And if an attribute can be found in the request scope then the jsp does not care about what's in the session.
When you use #SessionAttributesand #ModelAttribute (or Spring MVC in general) then avoid using HttpSession or HttpRequest. Even HttpResponseis of limited use. Embrace the beauty of Spring MVC instead :)
model.addObject puts object to the request scope while HTTPsession.setAttribute puts it to the session scope. And since variables on jsp are resolved on the next order: page scope -> request scope -> session scope -> application scope, you get what you get.
Java method params are passed by values. You can assign to this paramateter anything yoou want inside the method, but it won't have ny effect outside of it . Insisde of the method you're dealing with the copy of the param

In Spring MVC 3, how do I bind an object to a query string when the query string parameters don't match up with the object fields?

A 3rd party is sending me part of the data to fill in my domain object via a query string. I need to partially fill in my domain object, and then have the user fill in the rest via a form. I don't have any control over the query string parameters coming in, so I can't change those, but I'd really like to be able to use Spring MVC's data binding abilities, rather than doing it by hand.
How can I do this?
To add some complication to this, some of the parameters will require extensive processing because they map to other objects (such as mapping to a user from just a name) that may not even exist yet and will need to be created. This aspect, I assume, can be handled using property editors. If I run into trouble with this, I will ask another question.
Once I have a partially filled domain object, passing it on to the edit view, etc. is no problem, but I don't know how to properly deal with the initial domain object population.
The only thing I have been able to come up with so far is to have an extra class that has it's properties named to match the inbound query parameters and a function to convert from this intermediary class to my domain class.
This seems like a lot of overhead though just to map between variable names.
Can you not just have the getter named differently from the setter, or have 2 getters and 2 setters if necessary?
private int spn;
// Standard getter/setter
public int getSpn() {
return spn;
}
public void setSpn(int spn) {
this.spn = spn;
}
// More descriptively named getter/setter
public int getShortParameterName() {
return spn;
}
public void setShortParameterName(int spn) {
this.spn = spn;
}
Maybe that is not standard bean convention, but surely would work?

What, exactly, does a modelbinder do? How to use it effectively?

I was researching something and came across this blog post at buildstarted.com about model binders. It actually works pretty darn well for my purposes but I am not sure exactly whats going on behind the scenes. What I did was create a custom ModelBinder called USerModelBinder:
public class UserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
return user;
}
}
Then in my Global.asax.cs I have:
ModelBinders.Binders.Add(typeof(User), new UserModelBinder());
My understanding is that using the model binder allows me to NOT have to use the following lines in every controller action that involves a "User". So instead of passing in an "id" to the action, the modelbinder intercepts the id, fetches the correct "item"(User in my case) and forwards it to the action for processing.
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
I also tried using an annotation on my User class instead of using the line in Global.asax.cs:
[ModelBinder(typeof(UserModelBinder))]
public partial class User
{
}
I'm not looking for a 30 page white paper but I have no idea how the model binder does what it does. I just want to understand what happens from when a request is made to the time it is served. All this stuff "just working" is not acceptable to me, lol. Also, is there any difference between using the annotation versus adding it in Global.asax.cs? They seem to work the same in my testing but are there any gotchas?
Usually the Model Binder (in MVC) looks at you Action method and sees what it requires (as in, the objects types). It then tries to find the values from the HTTP Request (values in the HTTP Form, QueryString, Json and maybe other places such as cookies etc. using ValueProviders). It then creates a new object with the parameters that it retrieves.
IMO What you've done is not really "model binding". In the sense that you've just read the id and fetched the object from the DB.
example of usual model binding:
// class
public class SomeClass
{
public int PropA {get;set;}
public string PropB {get;set;}
}
// action
public ActionResult AddSomeClass(SomeClass classToBind)
{
// implementation
}
// pseudo html
<form action="">
<input name="PropA" type="text" />
<input name="PropB" type="text" />
</form>
if you post a form that contains the correct values (lets say you post a form with PropA and PropB ) the model binder can identify that you've sent those values in the form and build a SomeClass object.
If you really want to create a real working example you should use a strongly typed View and use HtmlHelper's EditorFor (or EditorForModel) to create all the correct names that MVC needs.
--
for reference MVC's default binder is the DefaultModelBinder, and some (there are more, you can look around in the System.Web.Mvc namespace) ValueProviders that it uses by default are the FormValueProvider and the QueryStringValueProvider
So, as I already said, how this basically works is that the default model binder reads the model that the action is recieving (say SomeClass in the example) reads what are the values that it can read (say PropA and PropB) and asks the ValueProviders for the correct values for the properties.
Also, if I recall correctly, you can also see the value providers in runtime using the ValueProviderFactories static class.
A ModelBinder looks at the arguments of the selected Controller action's method signature, then converts the values from the ValueProviders into those arguments.
This happens when the ControllerActionInvoker invokes the action associated with the ControllerContext, because the Controller's Execute() method told it to.
For more about the ASP.NET MVC execution process, see Understanding the MVC Application Execution Process

Need to allow an action method string parameter to have markup bound to it

I have an action method that takes in a string as its only parameter. The action method transforms it, and returns the result back to the client (this is done on an ajax call). I need to allow markup in the string value. In the past, I've done this by decorating the property on my model with [AllowHtml], but that attribute cannot be used on a parameter and the AllowHtmlAttribute class is sealed, so I cannot inherit from it. I currently have a work around where I've created a model with just one property and decorated it with the aforementioned attribute, and this is working.
I don't think I should have to jump through that hoop. Is there something I'm missing, or should I make a request to the MVC team to allow this attribute to be used on method parameters?
If you need to allow html input for a particular parameter (opposed to "model property") there's no built-in way to do that because [AllowHtml] only works with models. But you can easily achieve this using a custom model binder:
public ActionResult AddBlogPost(int id, [ModelBinder(typeof(AllowHtmlBinder))] string html)
{
//...
}
The AllowHtmlBinder code:
public class AllowHtmlBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var name = bindingContext.ModelName;
return request.Unvalidated[name]; //magic happens here
}
}
Find the complete source code and the explanation in my blog post: https://www.jitbit.com/alexblog/273-aspnet-mvc-allowing-html-for-particular-action-parameters/
Have you looked at ValidateInputAttribute? More info here: http://blogs.msdn.com/b/marcinon/archive/2010/11/09/mvc3-granular-request-validation-update.aspx.

Resources