We are migrating a struts application over to Spring MVC and utilizing the #Controller annotation to direct pages to various method calls.
I'm having trouble determining a good strategy for reuse though.
We basically do the same things in many of our pages:
prepareView(..., ...); //Various params -- could likely be standardized
if (!allowedToView()) {
mav.setViewName(injectedErrorPage);
}
performBusinessLogic(..., ...); //Various params -- not seeing how to standardize
persistEntities();
finalizeView(..., ...); // Various params -- could likely be standardized
What strategies are used for creating a final method which will allow the developers to "forget" about these processes? I'd thought about making an abstract class, but there really isn't a way I'm seeing to "standardize" this due to differences in what each method will take.
For instance we have the following:
#RequestMapping("params="assign", method=RequestMethod.Post)
public ModelAndView assign(#SessionAttribute(value="sessionAttr") Pojo pojo,
#ModelAttribute("command") CommandPojo commandPojo,
BindingResult result) {
//Follows pattern above
}
#RequestMapping()
public ModelAndView filterResults(#SessionAttribute(value="sessionAttr") Pojo pojo,
#RequestAttribute("requestAttr") String requestAttr,
#ModelAttribute("command") CommandPojo2 commandPojo2,
BindingResult result) {
//Follows pattern above
}
Having a final method would require this to be broken into two POJOs (which would then call the descriptive functions). My immediate concern there is how do we deal with different parameters coming into this final method? I don't see any way to handle this situation.
It'd be nice if we could still have this "final" method with protected functions which we could override where needed.
I have the same problem as you. I don't have a clean solution yet, but I believe that I made some progress so I thought I'd share with you what I have found so far.
I explored the use of interceptors as suggested by three_cups_of_java, but I run into various problems (described below). Currently I am trying to use a custom AnnotationMethodHandlerAdapter, but I am not done yet with this effort.
Interceptors
Since the interceptors don't have access to the controller object that they intercept (correction: they do have access to it, but with limited control over the execution flow), the controller and the interceptor have to communicate through objects in the session.
Here is a somewhat simplified example of what I mean:
In our old architecture, we have our own base controller that everyone extends. It itself extends MultiActionController, and adds some custom behavior - like in your example, updating a server-side view after post request before invoking the handler method. This works because all the controllers provide an implementation of a template method (e.g. getViewKeyInSession()).
Thus, the custom code in the base controller looks roughly like this:
// inside handleRequestInternal method
if (request.getMethod().equals("POST") {
updateViewAfterPost (session.get(getViewKeyInSession());
}
return super.handleRequestInternal();
Now, when we moved this code to the interceptor, we run into several problems:
The interceptor can't invoke getViewKeyInSession(), forcing us to use the same session key for all controllers (not good), or adhere to some convention that the session key for the view is based on the url or a param of the request (so far this is not good either).
Individual controllers can no longer override the behavior of updateModelAfterPost. This is usually not necessary, but unfortunately it was necessary for some controllers.
If the controller provides an implementation of updateModelAfterPost and wants to signal to the interceptor that it is not interested in the interceptor's help, it needs to do so by putting a marker object in the session for the interceptor to look at, and it needs to do it during the previous GET request (also not good and not flexible).
Using a Custom AnnotationMethodHandlerAdapter
Currently I am looking at specifying the DefaultAnnotationHandlerMapping directly in my xml (instead of mvc:annotation-driven) and then supplying it with a custom AnnotationMethodHandlerAdapter.
As I said earlier, I haven't made enough progress to present full results, however the direction that I am aiming at is this:
I think of AnnotationMethodHandlerAdapter as a Spring-supplied MultiActionController, but for pojo controllers. For example, I already know how to plug to it my own method resolver (see this question) and other Spring goodies.
This adapter has several methods that you can override, such as
invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler),
and maybe
handle(HttpServletRequest request, HttpServletResponse response, Object handler)
as well.
In your custom code, you can inspect the handler class, and then act accordingly. To continue my previous example, if the handler class has a method updateViewAfterPost or if it implements a certain interface, then you can invoke that method, and then call super to let spring proceed with the regular invocation. Thus, the code looks roughly like this:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// inspect handler object, look for marker interface, methods and/or annotations
// perform pre-processing on the handler object
// e.g. handler.updateViewAfterPost(request, response)
ModelAndView mav = super.handle (request, response, handler);
// post-processing on the handler object
return mav;
}
(Of course, this is just a toy example. In real code you'll need better exception handling)
UPDATE:
I tried the above strategy with a custom AnnotationMethodHandlerAdapter, and it indeed works. I used a marker interface on my pojo controller, and introduced only one new method named updateModelAfterPost to the life-cycle, and it works as expected.
There are several small caveats that I ran into, mainly because I was combining the old ways with the new ways in the same mvc context. Below you can see the changes I made to the xml context, followed by a list of the issues that I think are worth highlighting.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="2" />
</bean>
<bean class="com.sample.MyAnnotationMethodHandlerAdapter">
<property name="order" value="2" />
</bean>
<bean class="com.sample.MySimpleControllerHandlerAdapter" >
<property name="order" value="1" />
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="order" value="1" />
<property name="mappings">
<props>
...
</props>
</property>
</bean>
As mentioned in the comment, I unrolled the <mvc:annotation-driven> short-hand. I had to explicitly define two handler mapping, and also define two handler adapters.
Unfortunately in my legacy code some controllers are transcational and are proxied by cglib. The AnnotationMethodHandlerAdapter doesn't cope well with that, therefore I set the order of elements such that the legacy handler mapping and handler adapter act first, and the annotation-based handler mapping and handler adapter act second.
I had to define explicitly Spring's SimpleControllerHandlerAdapter, but I also had to extend it with my own class, because it doesn't implement the Ordered interface.
I had a problem defining the validator, because I didn't have the jar for jsr-303. Therefore I dropped the declaration of validators and conversion service. The above xml snippet is exactly what I use, it is not a trimmed-down version simplified for the sake of the answer.
and finally, here is the code for the relevant classes:
package com.sample;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
public class MyAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof MyMarkerInterface) {
MyMarkerInterface handler2 = (MyMarkerInterface) handler;
handler2.updateModelAfterPost(request);
}
return super.invokeHandlerMethod(request, response, handler);
}
}
package com.sample;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
public class MySimpleControllerHandlerAdapter extends SimpleControllerHandlerAdapter implements Ordered {
private int order = 0;
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
Could you implement a base class as you are suggesting and force a Template Method design pattern and also to borrow from what Nix said in his earlier comment to your question about leveraging a parameter collection in this base class?
Does that help?
If the parameters are varying from function to function I think #Nix suggestion of parameter collection is a good one. Alternatively you could use var arg of objects. But you might need to have a check to see if all parameters are present before a function is called like a Pre condition check.
Or maybe a combination of both like, you would know that some of the parameters are always needed and others optional. So use varargs for the optional likethe following for filterResults
public ModelAndView filterResults(#SessionAttribute(value="sessionAttr") Pojo pojo,
#RequestAttribute("requestAttr") String requestAttr,
#ModelAttribute("command") CommandPojo2 commandPojo2,
Object...restOfParameters){}
This could be combined with the template pattern that is disscussed earlier.
If you want reusability you really should look into spring webflow, if you haven’t already. In short, webflow is an advanced spring mvc contoller that allows better separation between you view layer and your business logic. The controller accepts request, maps and validates your model, delegates requests to the correct business services, and finally decides which view to render based on the outcome of services called, of state of the model.
Everything is configured through xml, which gives you a single point where all your webflow logic and navigation is located (there is an eclipse plugin to visualize the navigation if you don’t like xml). Spring webflow also plays nice with other mvc controllers if you need to handle some requests the old fashioned way. And last but not least, spring webflow adds some scopes for your variables that are very handy. Besides request , session and application, you also get flow and conversation scope, which is kind of like session scope, but only for the current application window. That means you can have multiple windows/tabs in your browser without any of these interfering with each other.
But you should check it out for yourself, there is a small reference guide available on the spring website, as well as multiple demo’s in their svn repo. Also, the book “spring in action” touches the subject of webflow. Hope this is useful.
http://www.springsource.org/webflow
Related
Thymeleaf dropped template expressions like #session, #httpServletRequest etc. in Version 3.1 (https://www.thymeleaf.org/doc/articles/thymeleaf31whatsnew.html).
We used those a lot in relatively large applications. I wrote an interceptor to populate those attributes at every request since I don't want to add them in every Controller needed (like described in the migration guide).
Does anybody know a better way of achieving this?
This is already the best method to populate attributes at each request, compared to the earlier methods defined in the Spring framework documentations.
I learned that #ControllerAdvice can be used in this case (see https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc).
Classes annotated with #ControllerAdvice, can have methods annotated with #ModelAttribute to populate attributes over multiple Controllers (all of them if not specified otherwise).
In my case:
#ControllerAdvice
public class CommonDataAdvice {
#Autowired
private HttpServletRequest request;
#ModelAttribute("request")
public HttpServletRequest populateRequest(){
return request;
}
}
I have a method annotated with #PreAuthorize(...) with some logic that goes away and queries an API for some information about what the user can view. However, I have this endpoint that I need to add this #PreAuthorize annotation into which receives in a more "complex" object which I want to transform slightly (the object contains an array that is some cases I want to add/remove data from).
#PostMapping("/search")
#PreAuthorize("#Service.isAuth(#searchParam)")
public ResponseEntity<Response> search(SearchParams searchParam) {
return service.getSearchResult(searchParam);
}
Is there a way I can modify searchParam inside the #PreAuthorize annotation then have it passed into the method body, I know that this is probably is not the correct way to do this and maybe isn't something that #PreAuthorize wasn't designed for but is there any way of doing this even with a different type of annotation. Obviously worst case I can move the logic into the method body but I would prefer to use an annotation-based solution like #PreAuthorize offers if possible. Thanks for any help even links to other relevant things would be useful I've not found much on google related to this.
I think the best solution is to make a handler/interceptor and then annotate it with #PreAuthorize. So I think you are in the right track but you need to make sure that you modify your code to implement the HandlerMapping interface to create the interceptor and then override the prehandle method. After you need to annotate it with #PreAuthorize programatically. The last thing will be to use a wrapper to modify the HttpWrapper, it cannot be done manually. Here links to the relevant resources in order:
Creating a Handler/Interceptor: https://www.baeldung.com/spring-mvc-handlerinterceptor
Using PreAuthorise in the interceptor: How can I require that all request handlers in my Spring application have #PreAuthorize
To modify the HttpServlet request you will need a wrapper: How to modify HttpServletRequest body in java?
Have a try, hopefully that works.
Snippet of code taken from second link uses a programatic PreAuthorize rather than annotation:
public class PreAuthorizeChecker implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
PreAuthorize annotation = AnnotationUtils.findAnnotation(hm.getMethod(), PreAuthorize.class);
//TODO use the technique shown on the third link to wrap and modify the HttpServletRequest
if (annotation == null) {
// prevent access to method wihout security restrictions
throw new RuntimeException("Rights are not defined for this handler");
}
}
return true;
}
.....
I need to make my logs (log4j engine) to be more informative with a unique id per request for my REST API in Spring-Boot application.
I want to avoid using a superclass which got requestId field and extend from it.
I tried to look for a good example over the web, but it wasn't so clear.
Is there any best practice that I can use?
Using a field for such a feature would just cause problems during the integration testing on the first glance..
Ideally, just follow an SRP principle and include the generation logic inside a dedicated class which you could make an injectable #Component.. MyIdGenerator etc.
There you could have a synchronized method generateId().
Now you could use it in whichever controller it is needed and also you could set-up your integration test more easily and have more control over them.
Update:
You could take also advantage of HandleInterceptorAdapter if this should be a global strategy:
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
/* generate unique id here and log what is needed */
}
You can encapsulate the ID generation and logging inside this class.
There are several best practices used as follows:
Use AOP Refer :AOP used for controller layer
Use MDC
Use MDC with AOP
MDC with AOP
I was trying to understand the caching that happens at the client side.
Unfortunately I am unable to find any resources that can help me out.
I have employee model objects which are fairly small in size.
Once a use a GET request to obtain an employee object, I want it to be cached at the client side
Now when the request comes again to obtain the same employee, I want to see if the actual object has been modified, if not, then serve from the client cache else return the modified object also adding it to the cache.
I am using Spring boot to create a REST endpoint.
What I have been able to figure out is that cache-control would be used some how, but I am not sure how the objects would be added here in spring.
Any help here is much appreciated!!!!
Thanks,
Amar
HTTP caching is not an easy topic. There are different ways to do it, and you should probably start by familiarizing yourself with the mechanisms, this seems to be a good starting resource: HTTP caching
Then, you will probably identify some common usage patterns you will want to reuse. One way to do that is to create custom annotations and write an interceptor that reacts on them.
For example, you could write such an annotation:
#Inherited
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface CacheFor {
long amount();
TimeUnit unit() default TimeUnit.SECONDS;
}
and use it on controller methods like this:
#CacheFor(amount=10, unit = MINUTES)
#RequestMapping(bla bla)
public FooBar serveMyData(){
// code here
}
and in your interceptor, you will need to look at the handler method, check whether it has this annotation, and if it does, set the appropriate headers.
I'm looking for a way to autowire HttpServletResponse. It doesn't work with spring out of the box, but I've found this description. This works but is sort of annoying, in that spring obviously has a mechanism to make objects request scoped (i.e. HttpServletRequest) and this seems to be a hack bolted on top.
Is there a way to hook into the same mechanism that spring uses for HttpServletRequest? And, any idea why spring team decided to only make HttpServletRequest autowire capable (and excluded HttpServletResponse)?
Perhaps there is some workaround, but it's not that obvious, because it's not the way it's meant to be. Spring MVC is meant to have singleton #Controller beans that provide #RequestMapping methods which take the request and response as arguments.
If you need the response in another place (the service layer) - don't do it. The response should not go beyond the web (controller) layer.
To inject the response, you need:
- to store the response in a ThreadLocal
- to make a factory bean that returns the current response
About the example code you showed - I'm not sure if you are not going to need the factory bean to return a proxy (implementing HttpServletResponse), which in turn to return the current response. And it gets rather complicated.
But ultimately - you should not do that. If you need to intercept multiple controller invocations, use an mvc-interceptor. If you really need to use an aspect, you can get the response if it is passed as argument to the intercepted method.
Can you simply include the request in the method handle?
#RequestMapping(method=Method.GET, value="myUrl")
public String doGet(HttpServletResponse response){//spring will put the response in for you
...
}