I usually add objects in my jsp requestScopes using Controllers.
For example, if I need to list categories in "localhost/products/viewall", I simply change my ProductsController adding something like
#RequestMapping("/products/viewall")
public void viewCategories(Model model) {
List<Category> categories = service.findAllCategories();
model.addAttribute("categories", categories);
}
so, this method adds a list of categories to my requestScope.
I need to do the same, but for all the pages of the website (since the variable I need will be used in the layout of the site).
How can I add something to all the pages requestScopes with Spring?
I think you have at least two possible options for this:
Using an MVC Interceptor. With an interceptor you can perform common operations for all requests. You can extend HandlerInterceptorAdapter and add common model data in postHandle
Using the #ModelAttribute annotation within an Controller. You can use this to add common data for all request mappings within a controller. You can also use an #ControllerAdvice (with #ModelAttribute annotated methods inside) if you want provide model data to all controllers. The section Using #ModelAttribute on a method should provide some additional information for this.
Related
In my current Spring project (using Thymeleaf), I have this two controllers HomeController and a generic controller extended to all controllers associated to the entities from the model layer.
In the HomeController, i have this modelAttribute mthod:
#ModelAttribute("usuario")
public Usuario usuario() {
return usuario.findBy("username", SecurityContextHolder.getContext().getAuthentication().getName());
}
Which obviously is acessible by the views mapped in this controller. Is there any way of share this modelAttribute with the views mapped in the other controller?
I read in many places about this RedirectAttributes resource, but I am guessing that only works with redirected views (like modelAndView.setViewName("redirect:welcome");)?
Move the method to a separate class and add the #ControllerAdvice annotation to that class. This way all the model attributes in that class will apply to all controllers in the application.
If you want to limit the set of controllers where the model attribute will be available you can specify in the #ControllerAdvice annotation that only controllers in specific packages or with specific annotations or implementing specific interfaces should be enhanced.
I am working on a spring base web application where, we have a few RestControllers and some Request DTO classes. Request DTO contains a token field which needs some validation. So I used spring validators to validate that. After validation, we want to send that field to an external system using another REST API (Just for some kind of analytics logging). The same field is repeated in multiple DTO objects and their controllers. So, I am easily able to define annotations for validators and reuse them across the DTOs. But I am not sure how to process that field after validation succeeds (i.e. call analytics API to consume that field post validation), without mixing it with the core logic of controllers.
Approaches I could think of:
Implement a filter/interceptor and process the field there. But then
there is a limitation that request body can be read only once so I
need to use some alternate ways by creating request wrappers.
Repeat the logic in every controller and it is very error prone as for
every new controller we need to remember to write that code.
But non of these approaches look cleaner. Can someone recommend a better way to achieve that?
Thanks in advance.
You can create a BaseController and implement the method there. Extend this BaseController wherever you need this logging service. Like below.
BaseController.java
class BaseController {
protected void remoteLogging(String name,String token) {
//Calling the remote log services}
}
AppController.java
#Controller
#RequestMapping("register")
public class LeaseController extends BaseController {
#PostMapping("new")
public String new(#Valid #ModelAttribute("registration") Registration registration,BindingResult result){
if(rest.hasErrors(){
remoteLogging("name","token");
}
}
I have a scenario where I have a list of filters that inherit from the same base class (BaseFilter). This are rendered in a few with different templates in a certain order based on what the user chooses. What is the best way to grab that data via javascript and submit it via an ajax request to a strongly typed List<IBaseFilter> in the ActionResult. Should I use a model binder and if so how with a list of IFilters?
public ActionResult Save(List<IFilter> filters)
{
}
public FirstFilter : BaseFilter {
// Has Two Properties
}
public SecondFilter : BaseFilter {
// Has One Property
}
Casting from child to parent means that you loose all child properties, and you only get parent properties. If you model bind filters to IFilter then only properties existing in IFilter would be available to you. I recommend that you create two action methods, and save filters consecutively, if possible, and use strongly-typed model binding in those actions. Another options might be to send data to the action method, but extract information manually from HTTP request, rather than letting ASP.NET MVC bind incoming parameters to your List<IFilter> parameter.
Basically Binders get what's in the FormCollection, and convert them to nice model objects for you. You probably can create a customized binder (IModelBinder implementation) to do all that for you. See this post to get an idea on how they work.
I have a controller that is 1600 lines long. This is mostly populated with a bunch of public methods with the #ModelAttribute annotation. It also has a few #RequestMapping methods.
I would like to bring down the line count and break-up this class. How do you handle multiple public methods with #ModelAttribute annotation? Aren't they all invoked whenever a request is processed?
I have a controller that is 1600 lines long
Gulp.
How do you handle multiple public methods with #ModelAttribute annotation? Aren't they all invoked whenever a request is processed?
When used to annotate a method, this annotation indicates that the method's return value should be used to populate the model for every request executed by that controller class, regardless of which #RequestMapping method is executed.
My suggestion is that you perform an audit to see which views (e.g. JSPs) use which model data provided by the various #ModelAttribute methods. It's likely that each view only uses a subset of that data.
Once you've figured out which combinations of #ModelAttribute and #RequestMapping methods go together, then break those up into individual classes.
If that doesn't fly (maybe all of the views really do use all of the data), then consider extracting the #ModelAttribute methods out of the class altogether, and stitch them together using a single method which amalgamates their outputs together manually (e.g. pass the Model or ModelMap object from the #RequestMapping method to this new method, which then adds the bits of model to that object.
Remember, #ModelAttribute-annotated methods are just a convenient way to add extra model data. They're not the only way.
#ModelAttribute methods can also return void:
#ModelAttribute
public void populateModel(Model model) {
model.addAttribute("key", "value");
// keep adding any number of attributes...
}
Can't you group multiple #ModelAttributes together? For example, if you have three methods one each for retrieving the values of three different select boxes, you could perhaps put them all into one method.
I'm a php programmer now doing a Java web project using Spring framework. I'm trying to organize my JSP files the way i would have organized my .tpl files in php.
So if it would have been php i would have done it like this:
index.tpl
includes one of layout.tpls (ajax.tpl, mobile.tpl, general.tpl, simplified.tpl . . .)
includes the header of the page
includes menus
includes the actual content of the page
includes the page footer
then from the php controller i would be able to do something like this:
setLayout('general');
showTopMenu(false);
setContent('mySexyPage');
beside that i would have organized my stuff so that my views (tpl files) will be organized in folderŅ each corresponding to a single controller. like this:
userManager
addUSer.tpl
editUser.tpl
editUserPermissions.tpl
articleManager
addArticle.tpl
editArticle.tpl
and in each controller somehow define from which folder to load my content template.
Now in Spring i have a controller with methods handling requests and each of the methods returning what the view should be. I can extend all my controllers from a single abstract class where i will create an instance of ModelAndView with all default values set, then request handling methods will add what they need to the instance their daddy already created and return it.
The problem with the above approach is that i'm not forcing the coder who's writing controllers to use the ModelAndView object i created, he way still return anything he wants from the handling method he wrote.
Is there some interface containing a method like ModelAndView getModelAndView() my daddy controller will implement so Spring will ignore whatever handler methods are returning?
Or is there some better way to do this ?
Content Template Issue
The Java world has a (more than one actually, but I'm sticking with the one I know) solution for this problem, it is called Tiles. check out section 16 of the Spring 3.0.5 Reference.
ModelAndView Issue
This is more interesting. First, you can use Model with out view and have your controllers just return the view name (i.e. return a String). I believe you want to create the initial Model somewhere. Then have each controller hander method accept an argument of type Model.
Here is what I tend to do (no claim that it is a best practice):
Have a Controller.get(Model model) method that sets the initial values.
#RequestMapping(method = RequestMethod.GET)
public String get(Model model)
{ ... set default stuff ... }
Every Handler method is a variation of this:
#RequestMapping(value = "/search", method = RequestMethod.POST)
public String search(Model model, ... other stuff as needed ...)
{ ... set stuff in model ... }