I am getting an token from request(the token is required to identify the user) to initialize user object in interceptor. Then i want to transfer this user object to controllers(i can put the user object to httprequest and get it in controller method, is this the best practice???, i am not sure) than transfer it to service and dao layer. But i don't want to add this user object as parameter to every method between controller->service->dao. What is the best practice of this?
Thanks in advance.
How about using a request-scoped bean to hold the token. You could reference the bean in your controller and set the token on it. Then in lower DAO layers could you reference the same bean to pull out the token. That would save having to pass the token down the method stack.
Alternatively you could use ThreadLocal storage directly which is effectively request scoped, but since you're using Spring, it would be cleaner and make more sense to leverage it's own request scope functionality.
Related
I'm having a hard time figuring out a specific time in which one would use #SessionAttribute over #ModelAttribute.
This question arose because after making a web application I realised that I have got a lot of methods that I passed in Principal principal to. In these methods, I use principal.getName() to get the username of the logged-in user and then retrieve the relevant data from the database using that username. In short, a lot of my methods needed access to the current user data and I resolved this in what I believe to be an inefficient manner.
To rectify this I was going to create a model attribute in a class annotated with #ControllerAdvice, in which I get the principal and get the user data from the database and add it to the model.
E.g model.addAttribute("currentUser", currentUser);
so that in the parameter list of these methods I can have (#ModelAttribute("currentUser") UserAccount currentUser)
saving unnecessary work by getting the principal and then proceeding to get the user from the database.
While I don't know a whole lot about #SessionAttribute, I feel like this sort of data(UserAccount currentUser) is more relevant to the session as opposed to the model. Am I Wrong?
I also heard that #SessionAttribute doesn't make its data available across multiple controllers which in this case I need. Hence why I'm using #ControllerAdvice.
My questions are as follows:
What is the best practice for implementing the above where I need to
repeatedly access the current users data. Maybe I can further increase efficiency by adding a current user bean on login and then use #Autowired so that I wouldn't even need to have currentUser in the parameter list. But I don't know if that's even possible. Is it?
Is it true that the method annotated with #ModelAttribute is called
prior to every #RequestMapping, #GetMapping, #PostMapping etc. call?
and that an object specific to #SessionAttribute remains in the
model for the duration of the session?
Also In what situation should I user #SessionAttribute over
#ModelAttribute?
The #SessionAttrributes annotation is for the use-case where you need to have a model attribute that you need to access over multiple screens. Like doing a checkout for a shopping cart, you would store the Order in the session, screen 1, confirm, screen 2 payment details, screen 3, delivery details, screen 4 OK. After screen 4 you would then call SessionStatus.setComplete() and it will clean that attribute.
That is the use case for #SessionAttributes and should be used in conjunction with #ModelAttribute. It is not intended to be used to store a, for instance, the user in the session for the duration of the HttpSession.
The #SessionAttribute (a different annotation!) is to retrieve an attribute from the HttpSession that was placed there earlier. In your case after authentication, you would place the User in the HttpSession with HttpSession.setAttribute("currentUser", user);. In a controller method, you could use #SessionAttribute("currentUser") User user to retrieve and use it. No need for an #ControllerAdvice or model attribute anymore.
However I would strongly to ditch your custom security implementation and use something like Spring Security instead. That way all that, and more, is already provided out of the box. In a controller method you can then use the #AuthenticationPrincipal annotation to retrieve the current user.
In my Repository, I'm calling external REST API and properly retrieve response which I wrap in ResponseEntity object as below:
ResponseEntity<ExternalModelResponse> response = restTemplate.getForEntity(baseUrl + "/api/externalObject", ExternalModelResponse.class);
However, ExternalModelResponse doesn't follow my Domain model so I want to introduce mapping of the ExternalModelResponse -> Domain model.
What would be the correct place to introduce such mapping? Should:
Repository method already return Domain object? That would imply mapping in the same method which fetched object from external REST API.
Repository method return ExternalModelResponse and let Service handle the mapping?
What is the most common place of such mapping?:
added as toDomainEntity method on ExternalModelResponse?
added as fromExternalModelResponse method on Domain Entity?
added as a method on Repository/Service ?
The service layer should use only domain objects, so I've done this mapping in the controllers.
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.
If I use Spring, which of these two methods is more correct.
Can I use the new() operator even if I use dipendency injection?.Can I mix both?
I would like to have some clarification on these concepts.
Thanks
First method:
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(new User());
return "index";
}
Second Method:
#Autowired
User user;
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(user);
return "index";
}
By using dependency injection does not mean that the use of new operator is automatically prohibited throughout your code. It's just different approaches applied to different requirements.
A web application in spring is composed of a number of collaborating beans that are instantiated by the framework and (unless overriding the default scope) are singletons. This means that they must not preserve any state since they are shared across all requests (threads). In other words if you autowire the User object (or any other model attribute), it is created on application context initialization and the same instance is given to any user request. This also means that if a request modifies the object, other requests will see the modification as well. Needless to say this is erroneous behavior in multithreaded applications because your User object (or other model attribute) belongs to the request, so it must have the very narrow scope of a method invocation, or session at most.
You can also have spring create beans with different scopes for you, but for a simple scenario of a model attribute initialization, the new operator is sufficient. See the following documentation if interested in bean scopes : Bean scopes
So in your use case, the second method is totally wrong.
But you can also delegate the creation of your model attributes to spring if they are used as command objects (i.e. if you want to bind request parameters to them). Just add it in the method signature (with or without the modelattribute annotation).
So you may also write the above code as
#RequestMapping(method=RequestMethod.GET)
public String create(#ModelAttribute User user){
return "index";
}
see also : Supported method argument types
If you want your beans to be "managed" by Spring (for e.g. to use with Dependency Injection or PropertySources or any other Spring-related functionality), then you do NOT create new objects on your own. You declare them (via XML or JavaConfig) and let Spring create and manage them.
If the beans don't need to be "managed" by Spring, you can create a new instance using new operator.
In your case, is this particular object - User - used anywhere else in code? Is it being injected into any other Spring bean? Or is any other Spring bean being injected in User? How about any other Spring-based functionality?
If the answer to all these questions is "No", then you can use the first method (create a new object and return it). As soon as the create() method execution is complete, the User object created there would go out of scope and will be marked for GC. The User object created in this method will eventually be GC-ed.
Things can be injected in two ways in a Spring MVC applications. And yes, you can you can mix injection and creation if doing right.
Components like the controller in your example are singletons managed by the application context. If you inject anything to them it is global, not per request or session! So a user is not the right thing to inject, a user directory can be. Be aware of this as you are writing a multithreaded application!
Request related things can be injected to the method like the used locale, the request, the user principal may be injected as parameters, see a full list at Spring MVC Documentation.
But if you create a model attribute you may use new() to create it from scratch. I will not be filled by spring but to be used by your view to display data created by the controller. When created in the request mapped method that is ok.
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.