I understand that the first call to getParameter will read the postdata content, if any.
Is there a way for me to limit how much postdata content would be processed into the RAM, or am I going to need to override the getParameter* methods for that to be accomplished?
I am not interested in making this a server-wide setting.
or am I going to need to override the getParameter methods for that to be accomplished?*
Yes.
For that you can use a homegrown HttpServletRequestWrapper which is injected by a Filter.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new MyPostDataLimitingRequest((HttpServletRequest) request), response);
}
I do not believe there is a way to limit through the existing getParameter(), which is a convenience method, without extending the servlet or adding a listener to break it down for you.
You can circumvent this by parsing the input stream within your servlet directly using getInputStream() or getReader(), but I believe this invalidates further calls to getParameter() for the rest of that request; you'll need to consume the rest of the input through your selected method.
It's not elegant, but it works.
Related
In spring I have a controller with an endpoint like so:
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public OutputStuff createStuff(#RequestBody Stuff stuff) {
//my logic here
}
This way if doing a POST on this endpoint, the JSON in request body will be automatically deserialized to my model (Stuff). The problem is, I just got a requirement to log the raw JSON as it is coming in! I tried different approaches.
Inject HttpServletRequest into createStuff, read the body there and log:
Code:
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public OutputStuff createStuff(#RequestBody Stuff stuff, HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
req.getReader().getLines().forEach(line -> {
sb.append(line);
});
//log sb.toString();
//my logic here
}
The problem with this is that by the time I execute this, the reader's InputStream would have already been executed to deserialize JSON into Stuff. So I will get an error because I can't read the same input stream twice.
Use custom HandlerInterceptorAdapter that would log raw JSON before the actual handler is called.
Code (part of it):
public class RawRequestLoggerInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
StringBuilder sb = new StringBuilder();
req.getReader().getLines().forEach(line -> {
sb.append(line);
});
//log sb.toString();
return true;
}
}
The problem with this tho is, that by the time the deserialization to stuff happens, the InputStream from the request would have been read already! So I would get an exception again.
Another option I considered, but not implemented yet, would be somehow forcing Spring to use my custom implementation of HttpServletRequest that would cache the input stream and allow multiple read of it. I have no idea if this is doable tho and I can't find any documentation or examples of that!
Yet another option would be not to read Stuff on my endpoint, but rather read the request body as String, log it and then deserialize it to Stuff using ObjectMapper or something like that. I do not like this idea either tho.
Are there better solutions, that I did not mention and/or am not aware of? I would appreciate help. I am using the latest release of SpringBoot.
To read the request body multiple times, we must cache the initial payload. Because once the original InputStream is consumed we can't read it again.
Firstly, Spring MVC provides the ContentCachingRequestWrapper class which stores the original content. So we can retrieve the body multiple times calling the getContentAsByteArray() method.
So in your case, you can make use of this class in a Filter:
#Component
public class CachingRequestBodyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
// Other details
chain.doFilter(wrappedRequest, servletResponse);
}
}
Alternatively, you can register CommonsRequestLoggingFilter in your application. This filter uses ContentCachingRequestWrapper behind the scenes and is designed for logging the requests.
As referenced in this post: How to Log HttpRequest and HttpResponse in a file?, spring provides the AbstractRequestLoggingFilter you can use to log the request.
AbstractRequestLoggingFilter API Docs, found here
I also tried to do that in Spring but i could not find way to pass my custom http request to chain so what did was,i have written traditional j2ee filter in that i have passed my custom http request to chain that is it then onward i can read http request more than once
Check this example http://www.myjavarecipes.com/how-to-read-post-request-data-twice-in-spring/
Ok, I have been trying to implement a system in which after check the parameters on the request, like the Path, i will actually modify the response to answer with different data. Idea is to create backend demo functionality, in this way client can Demo (NOT TEST) the application without actually making DB requests.
So, my first try was using servlet filters, a very good answer for this can be found here, and also some good document called The Essentials of Filters. But I could not make it work, i think because I'm using spring with #Controller and #ResponseBody, even I follow exactly same sample I would get a null as wrapperResponse.
Then I tried the Interceptors, here there is good example, and a good actually answer here. But the problem with this is that normally people will use the postHandle to modify the body, and I really do not want the handle to even trigger, because this means that the DB calls will be also triggered. And if I use the preHandler as here it will just make a new servlet, and I don't want that.
Finally I try #ControllerAdvice which basically allows you to re-write the body before is sent, but again, the handler gets processed and all DB calls with it.
MY goal, is that I do not have to put repeated code in each handler, I could make a preHandler insert some extra header and check that header in the #ControllerAdvice, but this means that i have to make some IF/ELSE in the handler so it doesn't get processed and I have to repeat that on the 100s of #Controllers that i have in the system, i want to be DRY.
I'm pretty sure the solution is on the filter in the way of this answer
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("BEFORE filter");
PrintWriter out = response.getWriter();
CharResponseWrapper responseWrapper = new CharResponseWrapper(
(HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
String servletResponse = new String(responseWrapper.toString());
out.write(servletResponse + " filtered"); // Here you can change the response
System.out.println("AFTER filter, original response: "
+ servletResponse);
}
But I can't make it work with spring and #ResponseBody calls. And true be told, this doesn't answer my question.
This is the way I manage to do this.
First I created an interceptor, which actually filter the request to pass just the want we want to demo. In the pre handler instead of trying to create a response there using the Response outstream I just used the RequestDispatcher to forward the request to a new controller, which I called Demo controller.
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
Pattern pattern = Pattern.compile("someregex");
Matcher matcher = pattern.matcher(request.getPathInfo());
if (matcher.find())
{
if (matcher.group(0).equals("SOMETHING"))
{
HandlerMethod handlerMethod = ((HandlerMethod)handler);
request.setAttribute("methodName", handlerMethod.getBeanType().getSimpleName());
request.getRequestDispatcher("/demo").forward(request, response);
return false;
}
return true;
}
else
{
return true;
}
}
In the Demo controller then you can create a proper response you want to demo. The good thing here is that in the new demo forwarded request will have an attribute for the original request javax.servlet.forward.request_uri, and that you can insert data, as the controllerName on the request before forward. All this data can be extracted in the Demo controller in order to generate the required data.
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.
I read the servlet-3.0 specification and have got one question about the ServletRequest object. Currenctly I have a filter chain
public class MyFilter implements Filter{
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
//do filter
}
}
after doing filters the javax.servlet.Servlet's
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
comes into play. Is it reliable that both in the filter's method and the service method operate on the same object reference? The servlet specification said this:
Each request object is valid only within the scope of a servlet’s
service method, or within the scope of a filter ’s doFilter
method, unless the asynchronous processing is enabled for the
component and the startA sync method is invoked on the request object
But it;s not obvious to me that the ServletRequest object is a singleton per one request handling.
Upd: To be more specific, I need to return the HttpSession instance within Filter's doFilter method and Servlet's do_HttpMethod_ method. Is it always the same? I mean httpServletRequest.getSession()
A container uses a single request object for a given request. However any filter can wrapper the request object so your filter or servlet may be getting a wrapper depending on what other filters do. Usually as an app developer you would know if this is the case. If no wrappers are used the filter and servlets get the same request object.
For info on request wrappers see:
http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequestWrapper.html
So when you call getSession() you may get the container implementation of the method or that provided by a wrapper. However note that an HttpServletRequestWrapper object provides a getRequest() method which returns the request object it wraps so you can recurse through wrappers until you get the original and then call it. Lots of examples how to do this on the web.
ServletRequest is an interface and the same is used in doFilter method as well as service method. So both are going to return the same session object (if you call getSession()).
Servlet as well as Filter uses same interface so it's going to behave same way. I am not sure why you are curious to know if it's singleton ? May be you can check servlet specification to know more about it.
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)!