I want to implement a before method for each controller in Spring Boot. So that the before method is invoked for any REST request.
You can use a Filter to intercept all incoming requests before handling them in the controller:
A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.
You can do something like that in your code:
#Component
public MyLogFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
System.out.println("Requested received and handled before controller");
chain.doFilter(req, res);
System.out.println("Code executed after controller");
}
...
}
You can also add logic to bypass the call to the controller if needed. For example if you have an authentication filter and you see that the request is not authenticated you can directly reply with a 401 code without calling the controller.
Related
I am making simple web app project in maven spring mvc with hibernate .I have set all controllers and all work fine .Now I am gonna make restriction for users who haven't logged in .I don't wanna let them to go inside before logging in.And also After they log out ,they should be thrown out.How can I do this ?
You can use a Servlet filter. This is an object that can intercept HTTP requests targeted at your web application.
A servlet filter can intercept requests both for servlets, JSP's, HTML files or other static content, as illustrated in the diagram below.
In order to create a servlet filter you must implement the javax.servlet.Filter interface.
public class SimpleServletFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
}
public void destroy() {
}
}
When a HTTP request arrives at your web application which the filter intercepts, the filter can inspect the request URI, the request parameters and the request headers, and based on that decide if it wants to block or forward the request to the target servlet, JSP etc.
It is the doFilter() method that does the interception. Here is a sample implementation:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
String myParam = request.getParameter("myParam");
if(!"blockTheRequest".equals(myParam)){
filterChain.doFilter(request, response);
}
}
Notice how the doFilter() method checks a request parameter, myParam, to see if it equals the string "blockTheRequest". If not, the request is forwarded to the target of the request, by calling the filterChain.doFilter() method. If this method is not called, the request is not forwarded, but just blocked.
The servlet filter above just ignores the request if the request parameter myParam equals "blockTheRequest". You can also write a different response back to the browser. Just use the ServletResponse object to do so, just like you would inside a servlet.
You need to configure the servlet filter in the web.xml file of your web application, before it works. Here is how you do that:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>servlets.SimpleServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*.simple</url-pattern>
</filter-mapping>
With this configuration all requests with URL's ending in .simple will be intercepted by the servlet filter. All others will be left untouched.
In my project i have multiple API's(implemented using Spring REST API).
Now i have this requirement that i have to manipulate the response in specific way before it is sent to client and changing it in every API method does not seems to be a good option.
Only solution i can think of is using servlet.Filter (by extending filter class)
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
> chain.doFilter(req, res);
and write my logic after
chain.doFilter(req, res);
but i am struggling to convert ServletResponse or HTTPServletResponse into HttpEntity.
Please help me how can i achieve this ? and is there any better approach available ?
Thanks
UPDATE
#jny solution has helped me.
little code snippet to show how it works.
#ControllerAdvice(basePackages = { "com.test.controller" }) // package where it will look for the controllers.
public class ResponseFilter implements ResponseBodyAdvice<Object> {
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
//here you can manipulate the body the way you want.
return body;
}
important is that your controller should be annotated with #ResponseBody
It depends on what exactly you need.
If you need to make changes of the body of the response,
if you use Spring 4.1 or higher, you can use ResponseBodyAdvice to manipulate the body of the response.
If you need to filter certain fields, there are other options available.
From documentation:
Implementations may be may be registered directly with
RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver or
more likely annotated with #ControllerAdvice in which case they will
be auto-detected by both.
This may point you in the right direction: Spring MVC: How to modify json response sent from controller
It involves wrapping the ServletResponse before invoking doFilter and using a custom ServletOutputStream that allows the response data to be manipulated after it would normally have been closed. HttpEntity is not involved.
For example, suppose I have this filter
#WebFilter("/api/*")
public class MyFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
and an interceptor
#Interceptor
public class HeaderLoggerInterceptor {
#AroundInvoke
public Object validateHeaders(InvocationContext ctx) throws Exception {
// pre-action
Object result = ctx.proceed();
// post-action
return result;
}
}
which object will be invoked first when a service endpoint is hit?
if I want to call setAttribute to request so some additional values can be written to local_access log. It doesn't work in Interceptor, only seems to work with Filter.
is it a bad idea to have both in the service?
which one is better to validate header?
which one is better to write response log before sending back to the client?
I really can't find document on this topic. thanks
I will answer my own question after my experiment
However, I discover another strange problem.
I am using an ExceptionMapper, which allows me to centralize the exception handling in one place;
When there is a missing header, I throw out a custom MissingHeaderException;
The exception will be caught by the Mapper, and we format it and provide a nice XML response back to the client
This logic works only with Interceptor not Filter. Any exception thrown from a Filter is not handled by the Mapper.
We're currently working on adding headers to each response from our application. To add these headers we're using the Servlet APIs Filter-interface.
We've got the following filter in our application:
public class SecurityFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
chain.doFilter(request, response);
HttpServletResponse httpServletResponse = ((HttpServletResponse) response);
httpServletResponse.addHeader("X-Frame-Options", "DENY");
httpServletResponse.addHeader("X-Content-Type-Options", "nosniff");
}
#Override
public void destroy()
{
}
}
This (specifically the doFilter-method) is implemented correctly according to the documentation, which suggests the following order of work:
Examine the request
Optionally wrap the request object with a custom implementation to filter content or headers for input filtering
Optionally wrap the response object with a custom implementation to filter content or headers for output filtering
Either invoke the next entity in the chain using the FilterChain object (chain.doFilter()),
or not pass on the request/response pair to the next entity in the filter chain to block the request processing
Directly set headers on the response after invocation of the next entity in the filter chain.
As far as we can see, the order of our doFilter-method is correct according to the documentation (pass the request to the chain first as stated under point 4, add custom headers afterwards as stated under point 5). However, the headers we add aren't visible in the responses. If we change to order to the following, everything seems to work just fine:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse httpServletResponse = ((HttpServletResponse) response);
httpServletResponse.addHeader("X-Frame-Options", "DENY");
httpServletResponse.addHeader("X-Content-Type-Options", "nosniff");
chain.doFilter(request, response);
}
Can anyone explain this behavior?
Oracle tells you to wrap the response before passing it down the chain, if you want to add something after chain.doFilter(..)
Note that if you want to preprocess the request object or postprocess
the response object, you cannot directly manipulate the original
request or response object. You must use wrappers. When postprocessing
a response, for example, the target servlet has already completed and
the response could already be committed by the time a filter would
have a chance to do anything with the response. You must pass a
response wrapper instead of the original response in the chain
doFilter() call. See "Using a Filter to Wrap and Alter the Request or
Response".
http://docs.oracle.com/cd/B32110_01/web.1013/b28959/filters.htm#BCFCIHAH
I do not know what version of the Servlet spec are you referring to, but in 3.1, chapter 6.2.1 "Filter Lifecycle" I read (emphasis mine):
After invocation of the next filter in the chain, the filter may examine response headers.
"Examine" not "set"! Actually, the spec for the header methods says (Servlet 3.1, chapter 5.2 "Headers"):
To be successfully transmitted back to the client, headers must be set before the response is committed. Headers set after the response is committed will be ignored by the servlet container.
I guess this is happening to your request, some servlet or filter down the chain is "committing", so headers are ignored.
Bottom line: The spec (at least as far as I can see in 3.1) does not suggest to set the headers after calling chain.doFilter(). Your second version that works is correct (and how I've always implemented filters that add headers)!
Why does the service() method in the Servlet interface not return an instance of ServletResponse but rather work on the ServletResponse object provided by the container?
In simple words why is the service method of the Servlet interface like:
public void service(ServletRequest request, ServletResponse response);
and NOT like:
public ServletResponse service(ServletRequest request);
If the response object is provided by the servlet container, it can control how things like buffering are handled. For example, suppose you created your own ServletResponse - how would the container manage the ability to stream the response if it's over a certain length, instead of buffering the data?
It uses a Response the container builds partially for it. It doesn't build the response out of whole cloth. It'd have to be an argument in any event.