I have a library method Common.addTheUsualStuffToTheModel(model) that needs to add various attributes to the model in every controller method in my app.
#RequestMapping(value = "/everypath", method = RequestMethod.GET)
public final String everyHandler(ModelMap model)
{
model = Common.addTheUsualStuffToTheModel(model);
return "everyPage";
}
So far I have been adding this same line to every handler method:
model = Common.addTheUsualStuffToTheModel(model);
But I'm afraid this is not consistent with the principle of "write once, use everywhere".
How do I avoid repeating this code in every handler?
You can use an interceptor and <mvc:interceptors> to do that
In your interceptor you can add anything as request attribute (which is in fact where the model attributes go). The interceptor code is executed before or after each method (that matches the interceptor mapping).
If you don't necessarily need the model to be populated before the controller method, in the postHandle method you get the ModelAndView object.
What about specifying #ModelAttribute annotated reference data provider methods. If you had a base class for all of your controllers, and that base class had #ModelAttribute annotated methods then I believe that data would be available in the model for all views handled by those controllers. Have a look at 15.3.2.8 in the Spring docs.
Related
I have a Spring RestController with an endpoint consuming JSON. It converts the JSON to an object, and validates the fields (using bean validation):
#RequestMapping(method = RequestMethod.POST, consumes = "application/json")
public ResponseEntity<?> myMethod(#Valid #RequestBody MyEntity e) {
...
}
I'd like to intercept the invocation of this method only after the conversion and validation has taken place to have access to MyEntity and possibly abort execution.
Using Spring interceptors, I can only intercept the request before it reaches the method - so I don't have access to the validated bean. I can use Spring AOP and add a pointcut, but is there a way of aborting the execution nicely – ideally without throwing an exception?
This functionality is outside of the business logic of this method, and is temporary – so I want to separate it.
A possible solution is to create a bean proxy between the Spring's proxy object and an object of your original class. To do that you need:
Implement your own BeanPostProcessor.
Spring will call its postProcessBeforeInitialization() method right after it has instantiated an object of your class, but before wrapping and initialising it. In this method, identify which beans must be provided with your functionality. It's often done by means of your custom annotations on methods or classes.
Create your own proxy for the beans from step 2. You can use cglib or something more modern for that. Your proxy will make all the checks you need and then call the parent's method if everything is ok, or just silently return from the method if something is wrong.
Return your proxy from postProcessBeforeInitialization(), it will instruct Spring to use it instead of the original bean.
As the result, Spring will create proxy of your proxy, not of the original object. When a request arrives, Spring will do the validation and send it to your proxy. Your proxy will make all your custom checks and decide on whether to send it further to your original class or to return immediately.
Typically the spring controller method has such view:
#RequestMapping("/url")
public String getUrl([params]){
// do some stuff..
}
As params spring can accept get/post/delete .. params from request or some standart params like model, locale etc.
My question is next: can spring accept my parametr when method is invoked. like:
#RequestMapping("/profile")
public String getUserProfile(UserEntity user, [params ..]){
// do some stuff..
}
Here UserEntity user is NOT a request body, but the entity from my DB.
Scenario is next: before executing method getUserProfile, spring execute some other method, that somehow get user from DB and pass it as method param.
Is there some way to repeat this scenario in real life?
This is possible if you use Spring Data. See the documentation: http://docs.spring.io/spring-data/commons/docs/current/reference/html/#core.web
Basically, what you have to do, is to add the id as parameter, and Spring Data will then use that ID to get the entity from the database and pass it to the method:
#RequestMapping("/{id}")
public String showUserForm(#PathVariable("id") User user) {}
To enable that feature, you have to put the annotation #EnableSpringDataWebSupport onto a #Configuration class
Check here.
We can use MVC interceptors. You need to modify the Interceptor implementation. Check the request URL pattern, if it matches then call a method that returns the UserProfile object. Hope this helps.
I have a problem with data flow in my app..
In the controller I am taking some model from DB, then I pass it to view - here some fields are shown (not all of them), and the user is able to modify them..
Then when he submits form, the controller should update the model in db.
The problem is flow, because not all of the fields are in tags, so they won't pass after submiting the form..
The only solution I found, is to create additional tags with all of the fields, which are not used in to pass them forward..
But in case I have many fields, for example - 30, I would have to create a lot of hidden fields...
What solution do you think would be the best?
Greetings,
M.
You have 2 options
Create a #ModelAttribute annotated method to get the model object from the database for each request
Put it in the session using #SessionAttributes.
#ModelAttribute annotated method
Instead of having a GET method filling the model you can also use a #ModelAttribute annotated method. This method will be invoked before each request handling method in the controller. One thing to take care of is that the id is passed in each request.
#ModelAttribute
public YourObject modelObject(#RequestParam long id) {
return yourObjectDao.findOne(id);
}
Now you can simply remove the filling of the model from the GET method and add a #ModelAttribute annotated method argument to your POST method. Which will then use the freshly obtain object.
Drawback of this approach is that when using optimistic locking it doesn't work so well anymore because each time you get the most recent version.
Using #SessionAttributes
Annotate your controller with #SessionAttributes this instructs the web handling to store the matching model objects in the session and retrieve them from there before binding.
#SessionAttributes("yourObject")
#Controller
public class YourController { ... }
Now in your POST method add an argument of the type SessionStatus and when everything is well call the isComplete method on that object. This will cleanup any session attributes put in the session by this controller.
public String handlePost(#ModelAttribute YourObject model, BindingResult result, SessionStatus status) {
if (result.hasErrors) {
return "yourView";
} else {
status.isComplete();
yourObjectDao.save(model);
return "redirect:your-new-view";
}
}
I'm developing a REST webservice with Spring MVC and I've implemented a custom annotation in order to annotate controller methods with it. This annotation may include a SpEL expression which I must evaluate considering controller method argument values. So, my idea is to implement a Spring MVC interceptor for this but the parameter HandlerMethod in the preHandle method is just a way to identify the method and does not provide access to controller method argument values. So, the only approach I can think of is to develop a Spring AOP aspect and intercept all the calls to annotated methods. By the way, I need access to the request, so if I go by the AOP way, all the annotated methods should include an argument with the request.
So, my question is: Is there any way to access the method argument values from thr Spring MVC interceptor or should I go the Spring AOP way?.
Thanks in advance.
You cannot use the controller method parameter in the preHandle method of an interceptor, because at the time of calling it, the parameters of the controller method have not been constructed (except for request and response).
So you will have to go the AOP way (do not forget to implement a method in your controllers ...) like explained in JavaBond answer. But thanks to spring framework, you can avoid that all the annotated methods should include an argument with the request. RequestContextHolder.getRequestAttributes() gives you a RequestAttributes object. If you know that your request is a HttpServletRequest, you can cast it to a ServletRequestAttributes and then access the native request via the getRequest() method :
RequestAttributes reqAttr = RequestContextHolder.getRequestAttributes();
HttpServletRequest req = ((ServletRequestAttributes) reqAttr).getRequest();
You should go the AOP way.
Write an Around advice against your custom annotation. The around advice should have a ProceedingJoinPoint argument. Using this you can get the annotated methods arguments values via proceedingJoinPoint.getArgs()
Sample advice shown below
#Around("#annotation(yourCustomAnnotation)")
public Object arooundAdvice(ProceedingJoinPoint joinpoint,
YourCustomAnnotation yourCustomAnnotation) throws Throwable {
Object args[] = joinpoint.getArgs();
// iterate over the args[] array to get the annotated method arguments
return joinpoint.proceed();
}
I am working on a wizard-like set of pages, where the user has to enter bits of data in several views for a final submission, allowing them to go back-and-forth prior to the final Submit is done. I was trying to use the same Bean, defined as a ModelAttribute, for all the views, basically just passing this one Bean around like a token in which each view adds its little bit of data.
The problem is that Spring MVC seems to create a new Bean on ever call. My admittedly fuzzy understanding about the Model was that it was basically like putting something into session, and that object would be around until the session was done. This does not seem to be the case.
So, I guess the first question is...where do Model Attributes "live", and for how long? Is there a better pattern out there for implementing a wizard-like interface using just Spring MVC (I am limited and can't use Web Flow...its not an approved tool where I work)?
It is NOT a good practise to use Model Attribute as a bean. It is good for manimulating form data before they are persisted into database.
#ModelAttribute("formAttribute") is created when you have specified it in your method as parameter:
public void getForm(#ModelAttribute("formAttribute") Form form) {
}
It is created before every method call by calling its contruct:
#ModelAttribute("formAttribute")
public Form getForm() {
return new Form();
}
When it is not specified in method parameter it doesn't exist.
There is possible to add your #ModelAttribute into session by defining #SessionAttributes on your controller:
#Controller
#SessionAttributes("formAttribute")
public HelloController
Then it is initialized once, when you firstly use it, and destroyed when you destroy it by calling:
public void finalStep(SessionStatus status) {
status.setComplete();
}
I think with combination of #SessionAttributes it is possible in relatively easy way create the wizard-like flow.
If Web-flow is not an option, you can try doing this:
Store your model attribute as a session attribute, this is accomplished by adding a #SessionAttribute annotation to your controller:
#Controller
#SessionAttribute("myconversationModel")
public class MyFlowController{
#RequestMapping
public String myMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel){
....
}
}
Then where you think you are done with the flow, just accept an additional parameter SessionStatus and call sessionStatus.complete, this will wipe the attribute from session
#RequestMapping
public String myFinalMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel, SessionStatus sessionStatus){
sessionStatus.complete();
....
}