Case-insensitive query string request parameters - java

My goal is that all below URI's should work
https://rest/xyz?sort=name
https://rest/xyz?Sort=name
https://rest/xyz?filter=name=value
https://rest/xyz?Filter=name=value
To achieve this, I have created custom filter that overrides the HttpServletRequest that is passed to the FilterChain. Below is the link for this approach:
http://forum.springsource.org/archive/index.php/t-87433.html
My code:
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class HttpCustomParamFilter implements Filter
{
private static class HttpServletRequestCustomeWrapper extends HttpServletRequestWrapper
{
private String[] parameterValues;
#Override
public String[] getParameterValues(String name)
{
Map<String, String[]> localParameterMap = super.getParameterMap();
// Handle case insensitivity of http request paramters like start, count, query, sort, filter etc.
if (localParameterMap != null && !localParameterMap.isEmpty())
{
parameterValues = new String[localParameterMap.size()];
for (String key : localParameterMap.keySet())
{
if (name.equalsIgnoreCase(key))
parameterValues = localParameterMap.get(key);
else
parameterValues = null;
}
}
return parameterValues;
}
public HttpServletRequestCustomWrapper(final ServletRequest request)
{
super((HttpServletRequest) request);
}
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// override the request passed to the FilterChain
chain.doFilter(new HttpServletRequestCustomWrapper(request), response);
}
#Override
public void init(FilterConfig filterConfig)
throws ServletException
{
// TODO Auto-generated method stub
}
#Override
public void destroy()
{
// TODO Auto-generated method stub
}
}
In this code, i have overriden getParameterValues(String name) method and achieved case-insensitivity of request paramters, but not sure if i need to override any other methods.
my doubts:
do i need to override other methods also like getParameter() and getParameterNames()?
what internal implementation is impacted with this?
which class i can see the code implementation of getParameter(), getParameterNames() and getParameterValues()?

First, let me say my peace: I don't think modifying the HttpServletRequestWrapper is the way to go. I am not even sure how you would go about using it, as my understanding is it's App Server specific. As a side note, this article has specifics on how to use the HttpServletRequest to get a case-insensitive query param without rolling your own.
But, in the spirit of answering your questions:
Do you need to override getParameter() and getParameterNames()? You could, as it would give you the ability to manipulate the case. In fact, I would say the safest way to make the query parameters case-insensitive would be to overwrite ONLY those methods. Make the getParameter() call do a case-insensitive equals on the string names. Not sure what you would do with getParameterNames(), maybe return every possible case, but this seems redundant.
What internal implementation is impacted by this? I am not certain. HttpServletRequest is so core to pretty much everything, there is no telling what you could introduce if your code is not 100% solid. For instance, Spring has a SecurityContextHolderAwareRequestWrapper, so does that mean you just broke Spring Security? No telling without a lot of testing.
Which class can I see the code implementation of getParameter(), getParameterNames(), and getParameterValues()? HttpServletRequestWrapper is the only implementation of HttpServletRequest interface, according to the JavaDocs. The actual implementation of this class is dependent on your application container. For instance, in my app its weblogic.servlet.internal.ServletRequestImpl, since I use Web Logic. Hopefully you are using an open-source app server that has the code readily available. The way I found this was to put a break in one of my Controller handler methods that has HttpServletRequest defined and viewing it's getClass() response in the debugger.

Related

How to check if unsupported parameter is contained in REST request?

Is there with Spring (boot) a way to check if a REST request contains a parameter not explicitly declared by the called REST method?
With the required flag we can force the client to include a certain parameter in the request. I am looking for a similar way to disallow the client to send a parameter that is not explicity mentioned in the declaration of the controller method:
#RequestMapping("/hello")
public String hello(#RequestParam(value = "name") String name) {
//throw an exception if a REST client calls this method and
// sends a parameter with a name other than "name"
//otherwise run this method's logic
}
For example calling
curl "localhost:8080/hello?name=world&city=London"
should result in a 4xx answer.
One option would be to explicitly check for unexpected parameters:
#RequestMapping("/hello")
public String hello(#RequestParam Map<String,String> allRequestParams) {
//throw an exception if allRequestParams contains a key that we cannot process here
//otherwise run this method's logic
}
But is it also possible to achieve the same result while keeping the same convenient #RequestParam usage as in the first example?
EDIT: Sorry, I do not see any connection to this question. The other question is about annotation processing at runtime. My question is about the behaviour of Spring's REST engine. Am I missing something?
EDIT: Based on the answers, I have written this HandlerInterceptor:
#Component
public class TooManyParamatersHandlerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod m = (HandlerMethod) handler;
if (m.getMethod().getName().equals("error")) {
return true;
}
List<String> allowedParameters = Stream.of(m.getMethodParameters())
.flatMap(p -> Stream.of(p.getParameterAnnotation(RequestParam.class)))
.filter(Objects::nonNull)
.map(RequestParam::name).collect(Collectors.toList());
ArrayList<String> actualParameters = Collections.list(request.getParameterNames());
actualParameters.removeAll(allowedParameters);
if (!actualParameters.isEmpty()) {
throw new org.springframework.web.bind.ServletRequestBindingException(
"unexpected parameter: " + actualParameters);
}
return true;
}
}
In this case you required HandlerInterceptor or HandlerInterceptorAdapter, override the preHandle method
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//request param validation validation
return true; //or throw exception
}
ServletRequest.getParameterMap() returns a map of key-values of the request parameters.
You can do it by ContainerRequestFilter feature which is added from JavaEE 7 that lets you access the resource class and resource method matched by the current request and make you to do your desire action when that have not been matched.
You can read more here :
https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ResourceInfo.html
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
#Provider
public class RequestParamFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Set<String> acceptedParamList = new HashSet<String>();
Method method = resourceInfo.getResourceMethod();
for (Annotation[] annos : method.getParameterAnnotations()) {
for (Annotation anno : annos) {
if (anno instanceof QueryParam) {
acceptedParamList.add(((QueryParam) anno).value());
}
}
}
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
for (String param : queryParams .keySet()) {
if (!acceptedParamList.contains(param)) {
requestContext.abortWith(Response.status(Status.BAD_REQUEST).entity("Unexpected paramter found : "+param).build());
}
}
}
}
P.N : Filters are cost in your application speed most of the times, Specially if you have complex chains in it!
I recommend to use it in this case (and similar cases) because of most of the those requests should not be reached to the server application at all.
I hope this helps you and Happy coding! =)
As far as I know, you cannot simply disallow parameters using Spring. Honestly, this issue is rather questionable and unnecessary and I think it's an antipattern.
However, Spring provides with each mapping the HttpServletRequest and HttpServletResponse objects to the controller method signature. Use the method HttpServletRequest::getParameterMap to receive the Map of the passed parameters for the further iteration and validation.
#RequestMapping("/hello")
public String hello(RequestParam(value = "name") String name, HttpServletRequest request, HttpServletResponse response) {
final Map<String, String[]> parameterMap = request.getParameterMap();
// logics
}
Passing those object to only to the #RequestMapping("/hello") allows performing the validation only to the selected mapping. If you want to define this behavior globally, I suggest you use HandlerInterceptor::preHandle as answered here.
If you make the hello parameter required=true, then you can just check the size of the Map whether is equal to 1 or not.

Spring MVC - Get HttpServletResponse body

Since after 2 days I still cannot figure how to perform a print of a HttpServletResponse body in HandlerInterceptorAdapter, I'll ask another time :)
With HttpServletRequest I can easily do something like request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); and I have the full body but how to make the same with HttpServletResponse?
I Had found lots of question on StackOverflow about that but none of them seems to work.
This is the handler:
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
//how to print here the "response" by using the "response" parameter
super.afterCompletion(request, response, handler, ex);
}
this answer is quite the same and links to this but they use ServletResponse and NOT HttpServletResponse and something with FilterChain which I have not in my afterCompletion handler. Even this that seems the most complete one is not suitable (I think) in my case.
Do someone can provide me a simple serialization example with HttpServletResponse?
It's been hard on searching deeply into it but found that ResponseBodyAdvice could be suitable for my purposes. So looking for some example on StackOverflow found this guy which had quite same issue having to manipulate the Object body.
That's my final working solution in order to implement what I wrote here
#ControllerAdvice
public class CSRFHandler implements ResponseBodyAdvice<Object> {
#Value("${security.csrf.enabled}")
private String csrfEnabled;
#Value("${security.csrf.headerName}")
private String csrfHeaderName;
#Value("${security.csrf.salt}")
private String salt;
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (new Boolean(csrfEnabled).booleanValue()) {
String csrfValue = SecureUtil.buildCsrfValue(salt, StringUtil.toJson(body));
response.getHeaders().add(csrfHeaderName, csrfValue);
}
return body;
}
}
Simple answer is "you can't do that in a Handler Interceptor".
Says so in the manual :
HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
So I advise you check out filter based solutions, as you pointed. You might be interested in : ContentCachingResponseWrapper Produces Empty Response which seems to accomplish what you want with minimal coding. But once you get started with filters, any of the well accepted answers that you have linked to in the question will likely do the job.
I ended up with the following in Kotlin:
#Bean
open fun logFilter(): CommonsRequestLoggingFilter {
val filter = InfoRequestLoggingFilter()
filter.setIncludeQueryString(true)
filter.setIncludePayload(true)
filter.setMaxPayloadLength(10000)
filter.isIncludeHeaders = false
return filter
}
import org.apache.commons.io.output.TeeOutputStream
import org.springframework.mock.web.DelegatingServletOutputStream
import org.springframework.web.filter.CommonsRequestLoggingFilter
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.PrintStream
import java.io.PrintWriter
import javax.servlet.FilterChain
import javax.servlet.ServletOutputStream
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpServletResponseWrapper
class InfoRequestLoggingFilter : CommonsRequestLoggingFilter() {
override fun beforeRequest(request: HttpServletRequest, message: String) {
logger.info(message)
}
override fun afterRequest(request: HttpServletRequest, message: String) {
// logger.info(message) - NOP, since doFilterInternal is logging it instead
}
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val outputStream = ByteArrayOutputStream()
val printStream = PrintStream(outputStream)
filterChain.doFilter(request, object : HttpServletResponseWrapper(response) {
#Throws(IOException::class)
override fun getOutputStream(): ServletOutputStream {
return DelegatingServletOutputStream(TeeOutputStream(super.getOutputStream(), printStream)
)
}
#Throws(IOException::class)
override fun getWriter(): PrintWriter {
return PrintWriter(DelegatingServletOutputStream(TeeOutputStream(super.getOutputStream(), printStream))
)
}
})
logger.info(String.format("%s ;status=%s ;payload=%s", createMessage(request, "", ""), response.status, outputStream.toString()))
}
}

Dynamic url mapping to different servlets in tomcat [duplicate]

Does servlet support urls as follows:
/xyz/{value}/test
where value could be replaced by text or number.
How to map that in the web.xml?
It's not supported by Servlet API to have the URL pattern wildcard * in middle of the mapping. It only allows the wildcard * in the end of the mapping like so /prefix/* or in the start of the mapping like so *.suffix.
With the standard allowed URL pattern syntax your best bet is to map it on /xyz/* and extract the path information using HttpServletRequest#getPathInfo().
So, given an <url-pattern>/xyz/*</url-pattern>, here's a basic kickoff example how to extract the path information, null checks and array index out of bounds checks omitted:
String pathInfo = request.getPathInfo(); // /{value}/test
String[] pathParts = pathInfo.split("/");
String part1 = pathParts[1]; // {value}
String part2 = pathParts[2]; // test
// ...
If you want more finer grained control like as possible with Apache HTTPD's mod_rewrite, then you could look at Tuckey's URL rewrite filter or homegrow your own URL rewrite filter.
As others have indicated, the servlet specification does not allow such patterns; however, you might consider JAX-RS which does allow such patterns, if this is appropriate for your use case.
#Path("/xyz/{value}/test")
public class User {
public String doSomething(#PathParam("value") final String value) { ... }
}
Or:
#Path("/xyz/{value}")
public class User {
#Path("test")
public String doTest(#PathParam("value") final String value) { ... }
}
(Related to: https://stackoverflow.com/a/8303767/843093.)
It does support mapping that url; but doesn't offer any validation.
In your web xml, you could do this....
/xyz/*
But that won't guarantee that the trailing test is present and that it is the last item. If you're looking for something more sophisticated, you should try urlrewritefilter.
http://code.google.com/p/urlrewritefilter/
You shouldn't be doing that in web.xml rather you can point every request to your filter (Patternfilter) and can check for URL
package com.inventwheel.filter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
/**
* Servlet Filter implementation class PatternFilter
*/
#WebFilter("/*")
public class PatternFilter implements Filter {
/**
* Default constructor.
*/
public PatternFilter() {
// TODO Auto-generated constructor stub
}
/**
* #see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* #see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String servletPath = ((HttpServletRequest)request).getServletPath();
String requestURI = ((HttpServletRequest)request).getRequestURI();
Pattern pattern = Pattern.compile(".*"+servletPath+"/(.*)");
Matcher matcher = pattern.matcher(requestURI);
if (matcher.matches())
{
String param = matcher.group(1);
// do stuff with param here..
}
chain.doFilter(request, response);
}
/**
* #see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
As stated above, base servlets does not support patterns like you specified in your question. Spring MVC does support patterns. Here is a link to the pertinent section in the Spring Reference Document.
No Servlet doesn't support patterns like that, possible approach as mentioned by other folks as well is to use /* after xyz but that doesn't check for {value} or /test. Better you go for Spring or JAX-RS. However if you plan to stick with Servlet a better way to write it:
#WebServlet(urlPatterns = {"/xyz/*"})
An answer from the year 2022.
Servlets do still not allow wildcards, so we can't do things like:
/xyz/{value}/test
Paul Tuckeys urlrewritefilter is still in version 4.0.3, and not compatible with the new jakarta namespace [1] (Version 5 is in development).
I found a solution in Tomcat itself, with its feature RewriteValve.
See https://rmannibucau.metawerx.net/post/tomcat-rewrite-url for a step-by-step manual. This is a convenient solution for allowing wildcards in the middle of a URL.
[1] https://github.com/paultuckey/urlrewritefilter/issues/239

ThreadLocale value getting mixed up in Servlet Filter

I am working on a messy Struts 1 application that makes use of a custom context class to store values throughout the application. Basically it is only used to store session scope variables. I guess the reason that this custom class is used is so that other classes which do not have access to the http session can still get and set the session variables.
Anyways, for the most part this works just fine. The custom context is used throughout the Actions and service classes to share variables with no problem. However, I just discovered that things do not work out so nicely using this custom context inside of an Http Filter! It appears that it randomly will pull the value from a different session. And by session, I actually mean thread, since this custom context uses ThreadLocale to do it's dirty work.
Take a look
package com.zero.alpha.common;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
public final class CustomContext implements Serializable {
private static final long serialVersionUID = 400312938676062620L;
private static ThreadLocal<CustomContext> local = new ThreadLocal() {
protected CustomContext initialValue() {
return new CustomContext("0", "0", Locale.getDefault());
}
};
private String dscId;
private String sessionId;
private Locale locale;
private Map<String, Serializable> generalArea;
public CustomContext(String dscId, String sessionId, Locale locale) {
this.dscId = dscId;
this.sessionId = sessionId;
if (locale != null) {
this.locale = locale;
} else {
this.locale = Locale.getDefault();
}
this.generalArea = new Hashtable();
}
public static CustomContext get() {
return ((CustomContext) local.get());
}
public static void set(CustomContext context) {
local.set(context);
}
public String getDscId() {
return this.dscId;
}
public String getSessionId() {
return this.sessionId;
}
public Locale getLocale() {
return this.locale;
}
public Serializable getGeneralArea(String key) {
return ((Serializable) this.generalArea.get(key));
}
public Serializable putGeneralArea(String key, Serializable value) {
return ((Serializable) this.generalArea.put(key, value));
}
public void clearGeneralArea() {
this.generalArea.clear();
}
public Serializable removeGeneralArea(String key) {
return ((Serializable) this.generalArea.remove(key));
}
}
Again, this seems to work just fine and dandy inside every other class other than a filter. Let me show you the filter where it messes up.
package com.zero.alpha.myapp.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.zero.alpha.common.CustomContext;
import com.zero.alpha.myapp.utility.CommonConstants;
import com.zero.alpha.myapp.utility.CommonHelpers;
import com.zero.alpha.myapp.UserDomain;
public class LoginFilter implements Filter {
public LoginFilter() {
}
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Don't use the login filter during a login or logout request
if (req.getServletPath().equals("/login.do")
|| req.getServletPath().equals("/login-submit.do")
|| req.getServletPath().equals("/logout.do")) {
chain.doFilter(request, response);
} else {
doFilter(req, (HttpServletResponse) response, chain);
}
}
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
// This is the problem right here. Sometimes this will grab the value of a different user currently logged in
UserDomain user = (UserDomain) CustomContext.get()
.getGeneralArea(CommonConstants.ContextKey.USER_SESSION);
if (session == null || user == null) {
// Unauthorized
response.sendRedirect(loginPage);
} else {
// Authorized
session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user));
chain.doFilter(request, response);
}
}
}
When the custom context is used to grab the user in the doFilter method, it will randomly grab the user object from another logged in user. Obviously not a good situation!
The only time this happens is after some activity from a different logged in user. I could sit there all day and keep refreshing user A's session and there wouldn't be an issue. However, after taking some action as user B and then refreshing user A's session again, it will usually be swapped. But then if I refresh user A's session again, things are back to normal.
I've noticed this happens extremely more frequently when the application is actually deployed to a remote development tomcat server. It still happens when running locally, but not nearly as often as when deployed remotely. It happens almost 100% of the time remotely.
I have examined the session variable inside of the filter, and there doesn't appear to be an issue with the session itself. I have confirmed that the session id is still correct even when the incorrect user is pulled from the ThreadLocale variable.
Can anyone help me out? Thanks.
Your strategy is irretrievably flawed. Unless your servlet engine is single-threaded, you cannot rely on the same thread to handle every request in a given session. Moreover, and more relevant to the problem stated, even when the same thread to handle one request in a given session is also used to handle the next request in that session, it is not safe to assume that it does not handle a request belonging to a different session in between. There is no guarantee of thread / session binding.
Anyway, you seem to be missing the obvious. If you want to store session-scope variables, then store them in the session. That's what session attributes are for. See HttpSession.setAttribute() and HttpSession.getAttribute() -- you can use these to set and retrieve your context object.

Java JAX-RS react on ANY endpoint invocation

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.

Categories