Difference between HandlerInterceptor and WebRequestInterceptor? - java

While writing interceptor for my application I noticed HandlerInterceptor and WebRequestInterceptor here.
I noticed that HandlerInterceptor's method take HttpServletRequest, HttpServletResponse, Object(handler) and other params
while WebRequestInterceptor take WebRequest (a wrapper of HttpServletRequest).
But I don't know what is the difference between these two interceptors.
Although seeing Spring API I can guess that WebRequestInterceptor can not commit response while HandlerInterceptor can do that. Please correct me here if I am wrong.

With a HandlerInterceptor you can:
Change the HttpServletResponse in preHandle method
Apply filters based on handler object. For example, you can apply some filters based on presence of some annotations on HandlerMethod
Prohibit the execution of the handler by returning false in preHandle method.
HandlerInterceptor can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale, theme changes or adding Cache-Control headers. Its main purpose is to allow for factoring out repetitive handler code.
One of the canonical use cases of WebRequestInterceptor is preparing context resources (such as a Hibernate Session) and expose them as request attributes or as thread-local objects. Also, you can modify those context resources after successful handler execution (for example, flushing a Hibernate Session). For example, OpenEntityManagerInViewInterceptor binds a JPA EntityManager to the thread for the entire processing of the request.
Although seeing Spring API I can guess that WebRequestInterceptor can
not commit response while HandlerInterceptor can do that. Please
correct me here if I am wrong.
WebRequestInterceptor interface is deliberately minimalistic to keep the dependencies of generic request interceptors as minimal as feasible. If you need to change the response, you should use HandlerIntercepter or Filters.

Related

HandlerInterceptor has an Object hander parameter, what types will get passed into the interceptor?

The HandlerInterceptor interface has a parameter Object handler, which means implementing code has to do type checking on the handler object in order to use it, and cast it as appropriate.
Code snippets I've found seem to assume the handler is always a HandlerMethod object and return true if this isn't the case, but I want to understand why this seems to be a common implementation to make a robust implementation.
A standard way of implementing this interface seems to be:
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod) {
// ...
return x; // whether or not to proceed with execution
}
return true; // fallback value in case handler wasn't a HandlerMethod
}
}
The Spring javadoc seems to ignore the fact that the interface has an Object type in it, it's very unhelpful for my understanding of this interface.
Some questions that might aid my understanding of how this interface is supposed to be implemented:
Why is true a sensible default if we get a handler that's not the object we expect it to be?
Which types can an HandlerInterceptor take for its handler parameter?
Under what circumstances could handler be a different type, if any?
Why is it an Object, not a HandlerMethod, parameter?
Basically the handler argument can be any type for which a HandlerAdapter exists. Most commonly used will be the RequestMappingHandlerAdapter utilizing a HandlerMethod.
But it could be a regular class, a servlet, or even a function (when using the functional approach). Spring Web Services also has an implementation as well as Spring Integration.
Spring itself will support the following out-of-the-box
HandlerMethod
HandlerFunction
Controller
Servlet
HttpRequestHandler
WsdlDefinition (Spring Web Services)
XsdDefinition (Spring Web Services)
So no it isn't always a HandlerMethod but it will be for most users.
Here's what javadoc of HandlerInterceptor#preHandle() says:
Interception point before the execution of a handler. Called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter invokes the handler.
DispatcherServlet processes a handler in an execution chain, consisting of any number of interceptors, with the handler itself at the end. With this method, each interceptor can decide to abort the execution chain, typically sending an HTTP error or writing a custom response.
This means interceptors are not supposed to touch the handler at all so they don't need to know anything about it. Their only job is to decide whether to pass the request further or not.
BTW, none of the standard HandlerInterceptor#preHandle() implementations in org.springframework.web.servlet.* try to analyze the true handler parameter type and do any logic based on it.
Why is it an Object, not a HandlerMethod, parameter?
From the org.springframework.web.servlet.HandlerMapping#getHandler() javadoc:
Return a handler and any interceptors for this request. The choice may be made on request URL, session state, or any factor the implementing class chooses.
The returned HandlerExecutionChain contains a handler Object, rather than even a tag interface, so that handlers are not constrained in any way. For example, a HandlerAdapter could be written to allow another framework's handler objects to be used.

Spring Boot disable #Cacheable per request

I'm trying to disable a Cacheable function in Spring Boot based on a URL parameter. For example http://myapplication.com/some/request?cache=false
I tried the condition with SpEL but I haven't been able to get it work
#Cacheable(value = "value", keyGenerator = "keygenerator", condition = "#{someComponent.isCacheEnabled()}")
Any suggestions? Thanks in advance!
user4843-
I am assuming your "arbitrary component" (i.e. someComponent) is a Spring managed bean in your Spring ApplicationContext?
If so, then you have not properly referenced this Spring managed bean using the SpEL expression specified on the condition attribute inside your #Cacheable annotation declaration. It should read...
condition="#{#someComponent.isCacheEnabled()}"
Notice the use of the # symbol on someComponent, which is explained in more detail here. And, although the Spring documentation is less than clear on the matter, it does imply the SpEL evaluation context created and used inside Spring's Cache Abstraction when processing the caching annotations uses "the built-in parameters", or SpEL conventions, in addition to the ones listed in Table 36.1 - Cache SpEL available metadata. See examples here.
I would also caution you on your use of some arbitrary component, or Spring managed bean to manage caching state for your entire application like this.
First, if someComponent is a Singleton bean (the default in the Spring container) then this bean would need to be Thread-safe, particularly as its state is changing, possibly and seemingly with every HTTP request, which leads me to...
I am not sure you want a Spring managed bean managing the "cacheable state" of your application service/repository methods if the caching state is conditional on each and every individual HTTP request, particularly since most HTTP containers process each HTTP request in a separate Thread. You are most certainly going to run into race conditions. In this case, I'd rather use an additional cacheable service method parameter to determine the cacheable state of that particular service method invocation, stemming from the HTTP request.
Example:
#Cacheable(value = "value", keyGenerator="keyGenerator" conditional="#enableCaching")
public <return-type> someCacheableServiceMethod(..., boolean enableCaching) {
...
}
In fact, I'd even suggest using more intelligible business-oriented rules to determine the cacheable state of your service methods.
Hope this helps!
-John

how can i convince spring 4.2 to pass OPTIONS request through to the controller

we have are using spring mvc with #RestController annotations on our controllers, and we're handling authorization in the controller. we use the same code to set the allowed methods in responses to CORS pre-flight request. to achieve this, we have:
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
in the configuration of the dispatcher servlet, and then we have:
#RequestMapping(value="/some/collections", method=RequestMethod.OPTIONS)
public void collectionOptions(
HttpServletRequest req,
HttpServletResponse res) {
List<RequestMethod> methods = new ArrayList<>();
// check actual permissions, add the appropriate methods
CORS.setAllowedMethodHeaders(res,methods);
}
we also have an interceptor that do basic checks on CORS pre-flight to see if the origin can possibly have any permissions at all.
we do it like this mostly because permissions for some requests actually depend on #RequestParams, i.e.:
OPTIONS /api/collections?userId=122
might be allowed if you have administrative privileges OR you actually the user with the ID 122. also, we have API keys, so
OPTIONS /api/collections?userId=122&apiKey=ABC
might be OK for one origin, but not for another one.
this works fine, but spring 4.2 now decides wether or not it handles an OPTIONS request, via a call to:
CorsUtils.isCorsRequest(request);
in AbstractHandlerMapping and then returns
HandlerInterceptor[] interceptors = chain.getInterceptors();
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
instead of the HandlerMethod ...
what we would need is some way to tell spring to let the controller handle OPTIONS requests, no matter what preflight request handlers are present.
We don't seem to be able to find a point where we can EITHER tell the built-in CORS handling to be quiet or to configure some subclass somewhere that would allow us to bypass the newly added code in:
AbstractHandlerMapping.getHandler(HSR request)
Is this in any way possible? Wouldn't it be nice for a feature like this to be quiet until I actively enable it (through WebMvcConfigurerAdapter or via those #CrossOrigin annotations)?
-------- EDIT -------------
the HTTP standard says the following about the OPTIONS method:
The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
thinking beyong just CORS, i think that intercepting a CORS options call although a corresponding method is mapped on the controller is not the right way to go. yes, CORS is one thing you can do with an OPTIONS call. but it is by no means the only one.
if there is nothing mapped and if a handler method is mapped with a different request method and the #CrossOrigin annotation, the assumptions that i want the built in CORS support to be triggered would be fine, but i do not think that any request that has the origin header set should automatically ONLY go to the CORS handlers.
I just convinced Spring 4.3 to pass CORS preflights to my controller by adding a custom handler mapping:
public class CorsNoopHandlerMapping extends RequestMappingHandlerMapping {
public CorsNoopHandlerMapping() {
setOrder(0); // Make it override the default handler mapping.
}
#Override
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
return chain; // Return the same chain it uses for everything else.
}
}
Note: you'll still need to tell Spring to dispatch OPTIONS requests to your controller to begin with - that is, set dispatchOptionsRequest to true in dispatcherServlet like it says in this question.
WHY IT WORKS
Sébastien's above answer suggests using your own CorsProcessor. As far as I can tell, this will still not use your controller as the handler; it will just pass a different CorsProcessor to its own CORS handler.
By default, it looks like the method AbstractHandlerMapping#getCorsHandlerExecutionChain will throw out your controller when it detects a preflight. Instead of using your controller as the handler, it instantiates a new PreFlightHandler and uses that instead. See Spring source code. This is the problematic line:
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
What it's doing here is rebuilding the execution chain with a PreFlightHandler instead of your controller. This is not what we want, so we can override it to return the input chain.
As suggested by zeroflagl, I also think that this is not a good idea to mix access control and CORS. And you should keep in mind that only preflight CORS requests are OPTIONS ones.
If you need to customize Spring CORS handling, you can use AbstractHandlerMapping#setCorsProcessor() to provide your own implementation that could eventually extend DefaultCorsProcessor.
Please notice that by default, CORS processing is enabled but no remote origin is allowed so customizing CorsProcessor is rarely needed. More info on this blog post.
Your access control check could be done as a regular HandlerInterceptor since a successful CORS preflight request will be followed by an actual request.

what's difference between Controller and Handler in Spring MVC?

The documentation of Spring MVC sometimes says about "handlers" or "request handlers". For instance, http://docs.spring.io/autorepo/docs/spring/4.0.4.RELEASE/javadoc-api/org/springframework/web/servlet/handler/SimpleUrlHandlerMapping.html says:
Implementation of the HandlerMapping interface to map from URLs to request handler beans
And sometimes it says about controllers. For instance, there is an interface called org.springframework.web.servlet.mvc.Controller ( http://docs.spring.io/spring-framework/docs/2.5.x/api/org/springframework/web/servlet/mvc/Controller.html ).
My question is: are Controllers and Handlers the same?
Generally speaking, a Controller is Handler, but a Handler doesn't have to be a Controller.
For example, HttpRequestHandler, WebRequestHandler, MessageHandler are all handlers which can work with the DispatcherServlet. ( (#)Controller is a handler for executing a web request and returning a view.)
Shortly, Handler is just a term, it is neither a class nor interface. And it is responsible for executing the Mapping.
A Controller is a specific type of Handler but not all Handlers are Controllers.
To execute a type of Handler there is a HandlerAdapter and for each type of Handler there is a different HandlerAdapter. You have Controller and #Controller, HttpRequestHandler and also a plain Servlet can be a Handler. Or if you have some custom things you can even implement your own.
Handler is a inclusive i.e. covering all the services details.
Controller is an an exclusive implementation.
In Spring we have the following different types of handlers:
HandlerMapping: The HandlerMapping strategy is used to map the HTTP client request to some handler controller(or controllers) and/or method. This is done based on the request URL and the HTTP method, but may also include the request parameters, request headers, or other custom factors.
For example: DefaultAnnotationHandlerMapping, SimpleUrlHandlerMapping, BeanNameUrlHandlerMapping.
HandlerAdapter: The DispatcherServlet uses a HandlerAdapter to invoke a method. Which is decouples the DispatcherServlet from controller implementations classes.
For example: AnnotationMethodHandlerAdapter, HttpRequestHandlerAdapter, RequestMappingHandlerAdapter, SimpleControllerHandlerAdapter, SimpleServletHandlerAdapter

How to set objects on Spring 3.1 MVC controller from an interceptor?

I'm having a couple of issues related to interceptors since upgrading to 3.1. In version 3.0.x and earlier I used the following pattern to intercept Spring MVC controllers:
Create an interface called something like RoleAware which defines one or more setters.
Have one or more controllers implement the interface
Register a new global interceptor which does a "handler instanceof RoleAware" check in the preHandle
If the interceptor is an instanceof RoleAware, then set one or more objects on the implementing controller
The first issue is that something changed in 3.1 so the instanceof check fails. I've fixed this by using the new explicit elements in my servlet context configuration. Not a big deal and a bit cleaner approach than the instanceof check.
The second issue is that when I attempt to cast the handler (Controller) to my RoleAware interface I get a ClassCastException.
I'd like a solution that will enable me to continue to arbitrarily set objects on the intercepted controllers. For example, set a Role object on any Controllers that are intercepted. Also, I'd like to know more about the changes in 3.1 that is causing this to break.
The reference doc says:
When using the RequestMappingHandlerMapping the actual handler is an
instance of HandlerMethod which identifies the specific controller
method that will be invoked.
So I guess that you should just cast the handler to HandlerMethod, call its getBean() method, and check is the returned bean is an instance of RoleAware.
Not tested though. You could try using a debugger to inspect the handler argument and see what it is if it is neither the handler itself, nor the HandlerMethod.

Categories