I have a class implementing a JAX-RS endpoint, as per below:
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED})
#Path("/site/")
public class ApiSiteResource extends AbstractContentResource {
...
#GET
#Path("/article/")
public Map<String, Object> getArticle (#Context HttpServletRequest request, #Context HttpServletResponse reponse, #BeanParam ApiParams params) {
//do stuff
}
#GET
#Path("/category/")
public Map<String, Object> getCategory (#Context HttpServletRequest request, #Context HttpServletResponse reponse, #BeanParam ApiParams params) {
//do stuff
}
What I need is to perform common processing (for example, capture analytics data) when any of the endponts of the above REST class is invoked, e.g., both for /site/article/ and /site/category/. I'm ideally looking for a solution that would be invoked at the end of the method execution, and ideally with least possible change to the existing methods code, so adding another method call at the end of the method is not the best option as that leads to too much code coupling. Ideally, I would like processing to be fired from an external class.
Is there a way how that could be done?
I am using a method of the Resource class that is annotated with the #Context Annotation and has a parameter that is injected from the context scope.
/**
* This method is called by JAX-RS for each request before
* the identified resource method is invoked, since it is
* annotated with the Context Annotation and carries a
* context-scope parameter which is injected.
*/
#Context
public void setServletContext( ServletContext servletContext ) {
...
}
(If you remove the ServletContext parameter, the automatic invocation on each resource call vanishes - at least in Jersey.)
Furthermore, you can put this method in a base class, say DefaultResourceImpl, which your Resource classes can extend, so you have this for all your Resource classes.
You can use JAX-RS Filters and Interceptors
For example there exist Request filters and response filters. You may do some stuff there:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
public class PoweredByResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}
Thank you all for your useful replies and comments.
Actually, capturing analytics was just half the story. In fact, I've also needed to add response headers.
So, I ended up implementing a filter as below:
public class ApiResourceHeadersFilter extends OncePerRequestFilter {
public ApiResourceHeadersFilter() {
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Headers", "accept");
response.setHeader("Access-Control-Allow-Methods", "GET OPTIONS");
filterChain.doFilter(request, response);
}
}
Plus added a mapping in the web.xml:
<filter>
<filter-name>ApiResourceHeadersFilter</filter-name>
<filter-class>com.workangel.eap.filters.ApiResourceHeadersFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ApiResourceHeadersFilter</filter-name>
<url-pattern>/api/site/*</url-pattern>
</filter-mapping>
Works like a charm; no messy code dependencies or modification. I'm sure I can extend it further should I need to collect analytics data as well.
Related
I have a series of Rest API Controllers in my Spring boot application with Request Mappings that match certain URLs.
I need to change my implementation to always make sure that a specific custom header is in place for all requests. If header is not there I want to fail the request. If it is I want to forward to the appropriate controller which would be the same as my current implementation.
Is there a way to do this in Spring Boot without modifying my existing controllers at all? Could I try to use something like Spring Security, even though my header is not related to security at all?
Thank you.
Web MVC defines an abstraction called "HandlerInterceptor" and its no-op implementation HandlerInterceptorAdapter
So you can register the bean that looks like this:
#Component
public class RequestProcessingTimeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// check the headers, extract them from request, whatever
return true; // if you want to proceed to controller
return false;// otherwise :)
}
}
This will instruct spring mvc to call the method before the flow gets to the controller.
You can configure a Filter as a #Service.
#Service
#NoArgsConstructor #Log4j2
public class FilterImpl implements Filter {
#Override
public void init(FilterConfig config) throws ServletException { }
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request.getHeader("required-header-name") != null) {
chain.doFilter(request, response);
} else {
log.info("Rejected {}", request);
}
}
#Override
public void destroy() {
}
}
I have an xml file (applicationCtx-security.xml) where all spring security filters and custom filters and their beans are defined.
I need to implement a custom filter which should be called after any http response, so that I can clear my User Principal after every http response.
Already tried SecurityContextPersistenceFilter
public class ResponseFilter extends SecurityContextPersistenceFilter implements Filter{
private static final Log log = LogFactory
.getLog(ResponseFilter.class);
public static final String USERNAME_KEY = "username";
public ResponseFilter() {
System.out.println("Inside constructor of ResponseFilter");
}
xml :
<beans:bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<beans:property name='securityContextRepository'>
<beans:bean class="com.web.ResponseFilter">
</beans:bean>
</beans:property>
</beans:bean>
My implemented code doesnot gets called.
ANy suggestions ?
You don't need to register a filter as a bean. But for it to work you have got to register your custom filter inside your Spring Security config, it has to have doFilter method overridden for some custom behavior, in your case output of the message - a constructor is designed only for the initialization of the class in Java Programming Language.
That's how it is done with annotations, you have to register this config as a bean in your XML file instead.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// constructor
#Override
protected void configure(HttpSecurity http) throws Exception {
// custom configuration of your http
//register the filter
http.addFilterAfter(new ResponseFilter(), ResponseFilter.class);
}
}
Also, don't forget to put your filter in the chain in the overridden doFilter method like this:
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain filterChain) throws IOException, ServletException {
// other code is omitted for brevity
//move on to the next filter in the chains
filterChain.doFilter(req, res);
}
SecurityContextPersistenceFilter is already a filter so you don't need to implement Filter and it is not the filter class that I would suggest to implement in the most of cases - it is better to extend GenericFilterBean instead in the most of the cases.
Your filter is missing doFilter you need to override that.
A sample filter will look like this
#Component
public class TransactionFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting a transaction for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Committing a transaction for req : {}",
req.getRequestURI());
}
// other methods
}
Whatever you put before chain.doFilter it will be executed before the servlet.
Everything you put after it will be executed after the servlet service() method returns.
So you can put your logic after chain.doFilter(request, response); call.
I have a question with Spring MVC RequestMapping annotation. need your help.
I have created one IPSLcontroller and i want that IPSLcontroller to handle all request url.i have created two method in this controller.
1)handleLogoutRequest :- this method should invoke on below url.
2)handleRequest :- this method should invoke on all request url otherthan logout.
http://localhost:9086/webapp/login
or
http://localhost:9086/webapp/add
or
http://localhost:9086/webapp/remove
here is my sample code. but it's not working as expected.
#Controller
public class IPSLController {
#RequestMapping(value={"/logout/*"},method = RequestMethod.POST)
protected void handleLogoutRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController logout request.......................................");
}
#RequestMapping(method = RequestMethod.POST,value={"/*"})
protected void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController all request Post.......................................");
}
}
You should use a general Prefix for every controller you use, so you can differ between them better. Also you donĀ“t need any "/" for calls like this.
#Controller
#RequestMapping("ispl")
public class IPSLController {
#RequestMapping(value={"logout"},method = RequestMethod.POST)
protected void handleLogoutRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController logout request.......................................");
}
#RequestMapping(method = RequestMethod.POST,value={"hello"})
protected void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController all request Post.......................................");
}
}
If you now want to call them over a ServletRequest or with a restService or something similar you should declare them like this
#GET
#Path("ispl/logout")
public void Method (HttpServletResponse ...)
Well it is working the way it should. You have a mapping for /* and for /logout/*. So when you post to /logout it invokes the method for /*. I suspect that if you post to /logout/something it would invoke your logout handler.
If you want it to work, you cannot have a wildcard mapping for the second method. At least use /something/* so that spring can make a correct decision on mappings.
So, let's have this simple controller:
#Controller
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
#RequestMapping(value="/entities", method = RequestMethod.GET)
public #ResponseBody ResultPojo getSomething() {
logger.info("getSometing");
return new ResultPojo();
}
}
...and the following context fragment:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Which basically means I want to be able to return nothing but json representations of the result bean, otherwise return 406.
If I send a GET request with accept=application/json, everything works fine, a json representation is returned in the http response with the 200 Ok status.
If I send a GET request with accept=application/xml, 406 is returned.
My problem in the second case is that even though 406 is returned eventually, the getSomething() method is still called (which I can see in the log). While this is no big deal for GET methods, it can cause confusion for POST methods (the resource is altered, but 406 is returned).
Is there a simple way to tell SpringMVC to check the accept header and return 406 before invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?
Is there a simple way to tell SpringMVC to check the accept header and return 406 before
invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?
the problem is I would have to put the produces clause to every
#RequestMapping in every controller. I'd like to set this on an
application level.
as far as I know there is no simpler method with SpringMVC. However, using standard JEE filters this is not very hard to do either. Just do something like:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class YourFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
if (request.getRequestHeader("Accept").contains("application/json")) {
chain.doFilter(req, res);
} else {
((HttpServletResponse)response).setStatus(SC_NOT_ACCEPTABLE);
}
}
public void init(FilterConfig config) throws ServletException {
// any startup stuff here if needed
}
public void destroy() {
// add code to release any resource
}
}
and:
<filter>
<filter-name>YourFilter</filter-name>
<filter-class>
path.to.YourFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>YourFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(didn't test the code, but it should be about right)
Maybe this is what you want:
#RequestMapping(value = "/entities", method = RequestMethod.GET, headers = {"content-type=application/json"})
methodName() {
...
}
I would like to know if it is possible to call a method for every matching HTTP request.
My case:
I need to send geolocation data in http headers, I send the request, it matches a defined operation and at the same time my geolocation is updated.
I can easily replicate a method call for each method in each class, but I would rather find a more adequate way to solve this problem.
Thank you for reading.
What you are looking for is a Jersey ResponseFilter. Here is a very simple example that adds a static header to every outgoing request:
package my.package;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
public class SimpleFilter implements ContainerResponseFilter
{
private static final String HEADER = "MyHeader";
public SimpleFilter()
{
}
#Override
public ContainerResponse filter(final ContainerRequest request, final ContainerResponse response)
{
response.getHttpHeaders().add(HEADER, "MyValue");
return response;
}
}
You can access various information through the request and response parameters to the filter method.
You will need to add the filter through your web.xml file to activate it:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>my.package.SimpleFilter</param-value>
</init-param>