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();
}
}
Related
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.
In a SpringBoot REST application I have a TableRequest type that contains column sorting, filtering, and paging details for GET requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters. As such it's applicable across many different controller methods. Also, because it applies to GET requests the fields are passed as request parameters (no #RequestBody json parameter). I've got a #ModelAttribute method inside the controller class that parses the request parameters into a TableRequest object, then the actual #RequestMapping method receives that object as a #ModelAttribute parameter.
Because the TableRequest class is generic, I'd like to be able to use it across multiple controllers without having to copy the parsing logic into every one. I'm wondering if there's a Spring-y annotation-based way of reusing the same #ModelAttribute method any time a controller has a TableRequest input parameter.
Thanks in advance :)
My Solution (based on selected answer below)
I created a #TableRequestController annotation and a corresponding #ControllerAdvice class that applies only to controller classes that have that annotation. That ControllerAdvice class includes the #ModelAttribute method tht parses the GET request parameters into a TableRequest object.
The one important caveat here is that the new #TableRequestController may only be applied to Controller class as a whole, not to individual controller methods. As such, I created a separate inner controller class, tagged with that annotation, whose #RequestMapping methods all accept a TableRequest object.
#TableRequestController:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface TableRequestController {}
ControllerAdvice class:
#ControllerAdvice(annotations = TableRequestController.class)
public class TableRequestControllerAdvice {
#ModelAttribute
public TableRequest tableRequest(
#RequestParam Map<String, String> params,
#RequestParam int pageStart,
#RequestParam int pageSize) {
return new TableRequest(params, pageStart, pageSize);
}
}
TableRequest REST controller class:
#RestController
#TableRequestController
public static class MyTableRequestController {
#RequestMapping("/the/table/request/url")
public MyResponse makeTableRequest(
TableRequest tableRequest) {
return new MyResponse(tableRequest);
}
}
You can use #ControllerAdvice. Everything defined here applies to all controllers, or a defined subset, if you prefer that.
Documentation
Another alternative (better imho) is to write a message converter. It only handles one specific type. You no longer need #ModelAttribute but simply have a TableRequest parameter in you controller method.
In a SpringBoot REST application I have a TableRequest type that
contains column sorting, filtering, and paging details for GET
requests for tabular data. It's generic in that it doesn't care what the specific data being requested is, it only specifies generic table parameters
This means what you have is a utility service.
So define the class as service and access it inside the views.
Access any beans in your application context using SpringEL’s syntax: ${#myBean.doSomething()}
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.
I have a controller class where i am adding an object to model, On view i can access it, and now i want to send this object back to a new controller method, Is it possible to do it with out using form? and example code is:
Here i am adding 'details' to model.
#RequestMapping(...)
public ModelAndView method1() {
.....
mv.addObject("details", details);
mv.setViewName(REVIEW_PAGE);
return mv;
}
I have an "Ok" button on review page where details are reviewed. Now i want to send this "details" object back to a new method for submission.
i want to access the details object in this second method of same controller class.
I have tried to add this as model attribute (as you can see in following code) but i am getting null values inside details object.
#RequestMapping(....)
public ModelAndView method2(#ModelAttribute("details") Details details){
//access details object here
}
The flow is like : ( add details in model (method1) --> send to view for review --> confirm (click ok) --> send back for submission (method2))
I am new to Spring MVC so if there are mistakes in my question, i am sorry for that.
You can tell Spring to keep a copy of the model on the server side by using the #SessionAttributes annotation on the controller
#Controller
#SessionAttributes("details")
public class TheController {
}
This comes with some caveats. The default built-in implementation is pretty basic and does not, for example, account for multi-tab browsers using the same session across tabs. It also has no automatic cleanup. You have to manually call session.setComplete() when you are done with the data.
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.