I'm using Spring MVC Framework and I'd like all the .jsp pages of the View to have access to the User's attributes(name, sex, age...). So far, I use the addAttribute method of the Model(UI) in every Controller to pass the current User's attributes to the View. Is there a way to do this only once and avoid having the same code in every Controller?
You can use Spring's #ControllerAdvice annotation on a new Controller class like this:
#ControllerAdvice
public class GlobalControllerAdvice {
#ModelAttribute("user")
public List<Exercice> populateUser() {
User user = /* Get your user from service or security context or elsewhere */;
return user;
}
}
The populateUser method will be executed on every request and since it has a #ModelAttribute annotation, the result of the method (the User object) will be put into the model for every request through the user name, it declared on the #ModelAttribute annotation.
Theefore the user will be available in your jsp using ${user} since that was the name given to the #ModelAttribute (example: #ModelAttribute("fooBar") -> ${fooBar} )
You can pass some arguments to the #ControllerAdvice annotation to specify which controllers are advised by this Global controller. For example:
#ControllerAdvice(assignableTypes={FooController.class,BarController.class})
or
#ControllerAdvice(basePackages={"foo.bar.web.admin","foo.bar.web.management"}))
If it is about User's attributes, you can bind the model bean to session as an attribute which can be accessed on every view. This needs to be done only once.
Another option could be is to implement a HandlerInterceptor, and expose the model to every request.
Related
I’m trying to develop a Spring MVC application, now I encounter a question. When login successful I add the User entity to session and call http://localhost:8080/user to get the session user. Everything is OK here. But if I call the URL like this http://localhost:8080/user?username=testuser then the session user's username will change to testuser. What should I do that just get current user from session?
The code likes below
Entity:
#Entity
public class User {
private Long id;
private String username;
// ...Getter and Setter...
}
Controller:
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController {
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(#ModelAttribute("current_user") User user) {
return user;
}
}
Response of http://localhost:8080/user
[{"id":1,"username":"aaa111"}]
Response of http://localhost:8080/user?username=testuser; it should be same as above, but is
[{"id":1,"username":"testuser"}]
The #SessionAttributes annotation isn't intended for this. Its intend is to store objects in the session during http requests. Imagine a lengthy database call to retrieve an object you don't want to retrieve this object each time but probably reuse an existing one. The object is to be intended to be used as a #ModelAttribute, this annotation indicates that you want to use this object for binding (i.e. you have a form to change attributes of the object). When you are finished with the editing of the object you should make this clear by calling setComplete() on the SessionStatus object. See also here.
You want to store an object in the session and retrieve it when you need it. For this use the HttpSession in the normal way of calling setAttribute and getAttribute. To obtain the current HttpSession you can simply add a method argument of the type HttpSession and it will be injected for you. (See here for a list of supported method arguments).
public void myRequestHandlingMethod(HttpSession session) {
User currentUser = (User) session.getAttribute("currentUser");
}
Or as you are already using Spring you could use the WebUtils for convenience. You can use the getSessionAttribute or getRequiredSessionAttribute methods to obtain the value from the session.
public void myRequestHandlingMethod(HttpServletRequest request) {
User currentUser = (User) WebUtils.getSessionAttribute("currentUser", request)
}
Another solution would be to extend Spring MVC. Spring MVC uses a HandlerMethodArgumentResolver to handle all the different types of method arguments. This mechanism is pluggable. You could create an annotation #CurrentUser and create a CurrentUserHandlerMethodArgumentResolver that will retrieve the user from the session and injects it in that place. You could then simply add your current user to your method signature.
public void myRequestHandlingMethod(#CurrentUser User user) { ... }
Configure the custom argument resolver
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.yourcomponany.app.web.CurrentUserHandlerMethodArgumentResolver />
</mvc:argument-resolvers>
</mvc:annotation-driven>
It also looks like you are rolling your own security framework, which I would advice against. Instead I would suggest using Spring Security instead. Advantage of this is that this provides integration with the Servlet API allowing for retrieval of the current Principal by either doing it yourself (request.getUserPrincipal()) or simply adding a method argument of the type java.security.Principal. It also comes with a custom HandlerMethodArgumentResolver which allows you to obtain the current Spring Security Authentication object.
try to get session value in controller from servlet request like below
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController{
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(HttpServletRequest request){
//false means do not create new session
HttpSession session = request.getSession(false);
return session != null?session.getAttribute("current_user"):null;
}
}
I have on user registration form, a startup controler (with request method get) that loads the user class to that form and a post method to save the user. Everything uses spring. But now I want to move the registration form to a popup in the header file and every page will import that.
The problem is, I don't want to insert the user class in all Get methods. What I really want is to "inject" in all models the user class without having to do something in all other methods.
Is this possible to do? And how?
In Spring 3.2, there is a #ControllerAdvice class level annotation introduced. If you place your model attribute in a separate class with #ControllerAdvice, it will be available to all controllers. For example add getUserForm() in the #ControllerAdvice annotated class, instead of your original controller(s):
#ControllerAdvice
public class ModelAttributeAdvice {
#ModelAttribute
public SearchForm getUserForm(){
return new UserForm();
}
}
I'm creating a Spring application and in it I have a side bar which can be use to access modules of the application.
The side bar remains visible through out all views of the application. I create the view using Apache Tiles.
Since I don't want to hard code the menu items in the JSP, I want to populate required data(Link name, URL pattern) from database.
I want to do this only once so my plan is to create a session bean to put all these menu details in to it.
I create a Class called "MenuDAO" and I autowire it to the controller. Then I create a method to return that "MenuDAO" object and I mark it as a "ModelAttribute" in my controller.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("userDetails")
public UserDetail getUserDetail(){
return this.userDetails;
}
Now I am wandering where can I fill this DAO with data. I want to do it once per session(like when user logged in).
I cannot do it in the method with request mapping because once it get called Model attributes are already injected so only when the page loads next time the new data will be available.
Again I thought of doing it inside a method annotated with #PostConstruct. But then I need to do it in every Controller.
So I want to know where I can do this correctly so that I can populate this bean once per user session.
Check this question it is quite similar :
Dynamic content in all page in Spring MVC
You will have to populate your modelAndView in a postHandle method of an interceptor.
The interceptors are called for every request so it is your responsability to not call your dao for every request.
I would suggest you to store your menu in a cache instead of putting it in the session (using ehcache which is easy to set up in spring) .
I solve it as follows. I don't feel that this is the most convenient way. But it works.
I autowire session bean and mark required attribute as model attrbutes.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("menuDetails")
public List<ElabModuleWebProperties> getMenuDetails(){
return this.menuDAO.getMenuList();
}
After authenticate I redirect to url patter "home" by Spring Security. In that method I check whether the session bean has initialized and if not I do it there.
#RequestMapping(value = "home", method = RequestMethod.GET)
public String showHome(ModelMap model, Principal principal){
logger.debug("User accessing home");
if(!menuDAO.isMenuInitiationDone()){
logger.debug("menuDAO is empty, populating data for the object");
menuDAO.setMenuList(loginService.loadUserDetailsByUsername(SecurityContextHolder.getContext().getAuthentication().getName()));
menuDAO.setMenuInitiationDone(true);
}
model.put("menuDetails", menuDAO.getMenuList());
return "welcome";
}
In other controllers I can just autowire the bean and use it. No problem.
Am #SessionAttributes for maintaining SpringMVC.
Say, #SessionAttribute("user")
Currently am passing the object as ModelAttribute in all the controller, which needs to use the SessionObject "user" like
Class controller{
public ModelAndView method1(#ModelAttribute("user")){ }
public ModelAndView method2(#ModelAttribute("user")){ }
public ModelAndView method3(#ModelAttribute("user")){ }
public ModelAndView method4(#ModelAttribute("user")){ }
}
Is this the only way??
or
Is there any other way? such that I can have a base controller, which can return the session object by just extending the the base controller.
What I've been using in some of my projects is this:
String user = (String) hsr.getSession().getAttribute("user");
If you're looking for some sort of authentication I suggest you start using spring security or other authentication mechanisms that can filter out pages according to roles or authentication status.
Not sure what your exact requirement is, but what about creating a filter/interceptor that reads the value from session and stores it in a ThreadLocal that can be accessed by controllers later
The controllers that need to access #SessionAttributes you need to add the annotation as shown below.
#Controller
#SessionAttributes({"user"})
public class Controller {
.............
}
HTH
In ASP.NET MVC in the controller I can just have an object from my model be a parameter in one of my methods, and a form submission that gets handled by that method would automatically populate the object.
eg:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(User u){...}
The user object will automatically be populated for be from the form submission.
Is there a way to have this automatically happen using Spring MVC, and if so how do I do it?
In Spring MVC (with Spring MVC 2.5+ annotation-based configuration) it looks exactly the same way:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView edit(User u) { ... }
The User object will be automatically populated. You may also explicitly specify the name of the corresponding model attribute with #ModelAttribute annotation (by default attribute name is a argument's class name with first letter decapitalized, i.e. "user")
... (#ModelAttrbiute("u") User u) ...
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/portlet/mvc/SimpleFormController.html#onSubmitAction(java.lang.Object)
Create a Form Controller, for example PriceIncreaseFormController and make it extend SimpleFormController
override the method public ModelAndView onSubmit(Object command)
there are many variants of the above. look for the right method that suits your need. For simple flow the above method should be sufficient.
Inside the method, you can typecast command and get your Command class.
commandObj = ((PriceIncrease) command)
commandObj will have the parameters populated by spring.
in your springapp-servlet.xml you should tell spring about the PriceIncrease command class as follows and also you should have a POJO for your command class created.
<bean name="/priceincrease.htm" class="springapp.web.PriceIncreaseFormController">
<property name="commandClass" value="springapp.service.PriceIncrease"/>
....
In Servlets no, but in Spring MVC absolutely. Take a look at the web framework docs.
Specifically Section 13.11.4, 9th bullet point.