I'm using Spring MVC 4 and I'm building a site with a template that requires several common components across pages, such as login status, cart status, etc. An example of controller function would be this:
#RequestMapping( path = {"/"}, method=RequestMethod.GET)
public ModelAndView index() {
ModelAndView mav = new ModelAndView("index");
mav.addObject("listProducts", products );
mav.addObject("listCategories", menuCategoriasUtils.obtainCategories());
return mav;
}
What would be a good way/pattern to feed these elements that do not belong to the controller we are currently calling so we don't repeat over and over unrelated operations in every method of every controller?
Thanks!
There is a several approaches to show common data in views. One of them is using of #ModelAttributte annotation.
Lets say, you have user login, that needed to be shown on every page. Also, you have security service, where from you will get security information about current login. You have to create parent class for all controllers, that will add common information.
public class CommonController{
#Autowired
private SecurityService securityService;
#ModelAttribute
public void addSecurityAttributes(Model model){
User user = securityService.getCurrentUser();
model.addAttribute("currentLogin", user.getLogin());
//... add other attributes you need to show
}
}
Note, that you don't need to mark CommonController with #Controller annotation. Because you'll never use it as controller directly. Other controllers have to be inherited from CommonController:
#Controller
public class ProductController extends CommonController{
//... controller methods
}
Now you should do nothing to add currentLogin to model attributes. It will be added to every model automatically. And you can access user login in views:
...
<body>
<span>Current login: ${currentLogin}</span>
</body>
More details about usage of #ModelAttribute annotation you can find here in documentation.
Related
Basically, I'm interested whether it's intended that the only models Swagger shows in swagger-ui are models used in RestController methods. It detects both my DTOs that I filled with #RequestBody, but it does not detect the User model, even with the ApiModel annotation. How to I go around this without making a dummy controller method?
For example:
#PostMapping("/signin")
#ApiOperation
public String login(
#ApiParam(value = "The login credentials DTO (username and password)", required = true)
#RequestBody
#Valid LoginCredentialsDTO loginCredentialsDTO) {
return userService.login(loginCredentialsDTO);
}
It detects the Model "LoginCredentialsDTO" because it was used here in the controller method.
Since I only use DTOs in my controller, it's not detecting my main model (User). I don't want to have to make a dummy method just for Swagger to be able to detect all my models.
Swagger describes the external interface of your api. When your User model is not used externally is will not be visible. See also swagger.io/docs/specification/2-0/basic-structure
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.
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();
}
}
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
i notice that this controller has now been deprecated in the latest spring and was wondering what the alternative controller is?
In Spring 3.0 you should use simple classes annotated by #Controller. Such controller can handle more than one request. Each request is handled by its own method. These methods are annotated by #RequestMapping.
One thing you need to rethink is the fact, that a old school SimpleFormController handle a lot of different requests (at least: one to get the form and a second to submit the form). You have to handle this now by hand. But believe me it is easier.
For example this Controller in REST Style, will handle two requests:
/book - POST: to create a book
/book/form - GET: to get the form for creation
Java Code:
#RequestMapping("/book/**")
#Controller
public class BookController {
#RequestMapping(value = "/book", method = RequestMethod.POST)
public String create(
#ModelAttribute("bookCommand") final BookCommand bookCommand) {
Book book = createBookFromBookCommand(bookCommand);
return "redirect:/book/" + book.getId();
}
#RequestMapping(value = "/book/form", method = RequestMethod.GET)
public String createForm(final ModelMap modelMap) {
modelMap.addAttribute("all", "what you need");
return "book/create"; //book/create.jsp
}
}
Annotated POJOs can act as controllers; see #Controller.
In Spring 3.0, your Controllers should no longer inherit from a base class.
The standard way is to use annotated controllers.