Unable to access nested object sent from ModelAndView - spring

In case of object which contains some other objects in it, When we add that in Spring MVC's ModelAndObject.addObject("myObject", obj) like this, and try to get that in JSP page, we are able to get the primitive varibale values perfectly fine. but when we try to get the inner object, spring somehow failing to access it's variables.
The only thing that we get is the reference of the inner object.

Related

Access data computed in filter for each request inside Spring Controller/Service Class

In our application, all rest apis are of the form:
http://{context}/{product_id}/{rest_url_path}
I have to verify the {product_id} inside a Spring Security Filter/SpringMVC interceptor, by fetching the ProductDetails from DB for the product_id. The ProductDetails will be used inside Spring Controllers/Service classes.
I don't want to fetch the ProductDetails again inside Controllers/Service. So I want to store the ProductDetails object somewhere for that RequestScope.
I have 3 approaches in mind. But each have their pros and cons. Please let me know which one better out of the 3. Also suggest any alternative approach.
Approach-1:
Save the ProductDetails object inside request attribute.
Inside Controller, i can easily get the HttpRequest. Inside Service, I can get HttpRequest by:
#Autowired
HttpServletRequest request;
or
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
if (attribs instanceof ServletRequestAttributes) {
request = ((ServletRequestAttributes) attribs).getRequest();
}
But I don't want to have HTTP request dependency inside Service to make to code more independent from WebLayer logic.
Approach-2:
Use any in memory cache based on product_id to stored ProductDetails
But this I think a over kill only for this use case. Increasing unnecessary dependencies of a cache.
Approach-3:
Store the Object in a ThreadLocal variable to store request scope data.
But not sure if it correct this way.
Let me know an efficient approach to solve this problem
1st and 3rd are suitable for your problem statment but first one is more elegant as data will stored only for current request scope and will get automatically lost when server send response. You can also use threadLocal but you have to be cautious ,if you forget to remove object it will hang around in an environment that uses a thread pool.
The first approach you mentioned is more efficient way to access the same data in both filter and controller even though you have to inject the dependency of HttpservletRequest in SpringController.
If the data is very user specific like other user will not have access to those data in that case you should use ThreadLocal.

#ModelAttribute returns a new instance on form submit

I am working on a Spring MVC based application. The process flow is as follows:
Fetch the data from DB (table mapped to a POJO)
Display a form backed by the POJO (from step 1). Not all the fields are displayed (like Primary Key etc).
User can update some field value in the form and will then submit.
On receving the updated POJO using #ModelAttribute annotation in my Controller, I found that not all the fields are populated in the POJO received via #ModelAttribute. All the fields which were not mapped on the JSP (like primary key) are set to null or their default value in case of primitives. Due to this I am not able to update the same in the DB.
One solution that I found is I can use fields but that does not sound much efficient solution as I have a large number of attributes which are not displayed on the JSP page.
A model attribute is simply a glorified request attribute. Request attributes only live for the duration of one request-response cycle.
HTTP request -> Get POJO from DB -> Add POJO to model attributes -> Render JSP -> HTTP response
After that, the request attributes are eventually GC'ed since they are no longer reachable by the application (the servlet container makes sure of that).
The next request you send will have its set of new request attributes with no relation to the previous requests'.
When you generate a <form> from a model attribute, Spring creates the <input> elements from the fields of the model attribute which you choose. When you eventually submit the form, only those values will be sent as request parameters in the request. Your application will therefore only have access to those to generate the new model attribute.
You seem to need Session attributes or Flash attributes (which are really just short-lived session attributes).
Please if somebody know a better solution please let me know, but on my application we are always sending back all those id´s and others values that we want to persist in the request response with hidden fields, but I think is a little bit risk, for example in case of id´s. which could be used for SQLInjections attacks.
You could use path variable to transport the primary key (kind of rest url ...) and make use of all the magic of Spring :
create a DefaultFormattingConversionService bean (to keep default conversions)
register (in that ConversionService) a Converter converting a String in your POJO (string -> primary key -> object from database)
directly use the path variable in your controller methods
#RequestMapping(value=".../{data}/edit", method=RequestMethod.GET)
public String edit(#ModelAttribute("data") Pojo pojo) {
return "view_with_form";
}
#RequestMapping(value=".../{data}/edit", method=RequestMethod.POST)
public String update(#ModelAttribute("data") Pojo pojo, BindingResult result) {
if (result.hasErrors()) {
return "view_with_form";
}
return "redirect:.../data/edit";
}
When you give a ModelAttribute to a controller method, Spring tries to find it in the session or in a path variable. And even if you didn't asked for it, the error management is not very expensive ...

How to prevent #ModelAttribute from creating command objects from request parameters?

I.e. I only want a nice way to retrieve existing objects from my Model (mostly some SessionAttributes).
I don't want new objects to be created and I especially don't want objects to be instantiated from request parameters and put into the model. This just sounds like a back door to me.
It would also be great if an Exception can be thrown if no matching parameter is in the model.
I got the answer to this by reading the source code. According to the implementation of org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveModelAttribute, a new bind Object will not be created if the name of the model attribute is declared as a session attribute using the #SessionAttributes annotation.
If the attribute is not present in the session, an Exception will be thrown.
So it is relatively safe to bind session attributes this way.

Best practice for using #SessionAttributes

I am trying to share data between two controllers in a Spring mvc application.
In Controller A I have annotated the class with #SessionAttributes({"mymodel1"}) and in the method which receives the first GET request I add it to the ModelMap:
model.addAttribute("mymodel1", MyModel1);
I now want to read myModel1 from Controller B.
In this Controller I have the following method which intercepts the POST requests and already has a different model in its parameters:
public String processSubmit(#ModelAttribute("mymodel2") MyModel2 mymodel2, BindingResult result, SessionStatus status, HttpServletRequest httpRequest)
Up to this point everything works fine and I am able to read mymodel2 from processSubmit however if I now want to add another #ModelAttribute("mymodel1") MyModel1 mymodel1 to this method signature I would have expected to be able to read the value I was setting in Controller A however I'm getting exceptions that the first model is no longer recognised.
So my question is: how can I read mymodel2 from Controller B?
You can't do that with #SessionAttributes :
Session attributes as indicated using this annotation correspond to a specific handlers model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handlers conversation.
For example I use this annotation when I want to validate elements with Hibernate validation, and after I submit the page and SOME elements are invalid I want the rest to be still on the page, but this is not your case. I think that the only way to do it would be with:
HttpSession.getAttribute()
The javadoc excerpt above is the most typical way #SessionAttributes is used. However, what Joly is describing should also work. Session attributes are stored via DefaultSessionAttributeStore, which by default does not prefix attribute names when it stores them in the session. That means if ControllerA and ControllerB both list an attribute called "mymodel1", they're actually referring to the same session attribute. You'll need to provide a little more information on the error you're getting and the actual controller code.

Spring - best way to deal with binding to a list of beans in a simpleformcontroller

Without thinking about it too much, I've been doing something like the following:
1) building a list of SomeBean objects based on the results of a database call
2) putting that list in my command object
3) building a form based on that command object where users can modify attributes of the SomeBeans
4) extracting data out of the post-submit command object and writing the updated data to my database
My code looks something like this:
public class UpdateThingsinListController extends SimpleFormController {
protected Object formBackingObject(final HttpServletRequest request)
throws Exception {
List<SomeBean> beans = database.getBeans();
Command comamnd = new UpdateThingsCommand()
command.setList(beans);
return command;
}
protected ModelAndView onSubmit(final HttpServletRequest request,
final HttpServletResponse response, final Object commandArg,
final BindException errors) throws Exception {
database.setBeans(commandArg.getList());
}
}
my jsp looks somthething like:
<form:form>
<c:forEach var="bean" items="${beans}" varStatus="status">
<form:checkbox path="beans[${status.index}].someBooleanProperty" />${bean.name} <br>
</c:forEach>
</form:form>
The code works fine, but it's just dawned on me that my "beans" list is getting created twice (sessionform must be false in my case) -- once when displaying the form, once when binding. If anything changes on the second creation (a bean is missing, the results are in a different order), my binding will get messed up, and I'll get fired. I'm beginning to think that any biding scheme where a command object needs to be merged with a form submission is very risky.
So, my question is -- how do folks ensure that form submissions get bound to lists correctly? Is there another way to do it besides list index? Object ids maybe?
thanks,
-Morgan
If the sessionForm is set to false then during the submit the command is recreated in formBackingObject method. Order of the list in the onSubmit method will be exactly like order of the binded objects on page.
If you can't depend on the List always being the same whenever you retrieve it, then you have to use a different collection - a map.
Another approach would be to cache your List somewhere. Perhaps some AOP around your database.getBeans method.
The quick way is to first delete all existing SomeBeans from the database and create the ones bound on submit.
A more elegant way might be to put the unique identifier of the SomeBean in a hidden form field.

Resources