Updating response after doFilter - java

I wish to add a HTTP header after the doFilter() function has finished running.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse) response;
try {
chain.doFilter(request, httpResp);
} finally {
httpResp.setHeader("ADD A HEADER: ", "HEADER");
}
}
It seems that doFilter flushes the response.
UPDATE:
Thanks. After viewing IgorMadjeric and richardtz answers I decided to try Response wrapper:
This is the wrapper:
public class BufferedHttpServletResponse extends HttpServletResponseWrapper {
public BufferedHttpServletResponse(HttpServletResponse response) {
super(response);
}
public void flushBuffer() {
System.out.println("flush");
}
}
This is the Altered code:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse) response;
try {
BufferedHttpServletResponse bufferedResponse = new BufferedHttpServletResponse(httpResp);
chain.doFilter(request, bufferedResponse);
} finally {
bufferedResponse.setHeader("ADD A HEADER: ", "HEADER");
}
}
Still does not working. Did I do something wrong?

you cannot modify the response of the header once it has been flushed/commited (it has already been sent to the client).
doFilter does not flush the response, but many things in the execution of the request may cause it.
(explicit call to response.flush(), too large body in the response), and you cannot control it.
However, you can use a HttpServletResponseWrapper to change this.
Hope it helps.

As already mentioned in the comments and in this question it might not be enough to overwrite flushBuffer(), but you need to also overwrite getWriter() and getOutputStream(). In certain cases even that might not be enough. For instance if the sendError(int) is called on the response it can also get commited.

In your case from code which you have posted, you can not to say:
It seems that doFilter flushes the response.
There is some rules about committing response.
Response is probably already committed by some component after you filter.
If you want to avoid this behavior you should use Request/Response wrappers, this will prevent components, on which your filter is applied to commit response.

Related

How can we get and set response body in Spring Boot Filter

I have a Spring MVC application which return ResponseEntity and clientResponse object as response body
#RestController
public class XxxController {
public void ResponseEntity(ClientRequest clientRequest) {
...
return ResponseEntity.ok(clientResponse);
}
}
But how can we get the clientResponse object or set a new Response body in Spring Boot Filter?
#Component
public class MyClassFilter implements Filter {
#Override
public void doFilter( HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Not sure what you mean by get Response in Filter. In a filter the request is yet to be passed to controller, so there is no response yet. You can get the request though. But be careful not to read the request as in that case the request stream will be read in the filter and when it arrives at the controller the entire request stream will be already read. To set the response you can do the following:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
response.resetBuffer();
response.setStatus(HttpStatus.OK);
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
response.getOutputStream().print(new ObjectMapper().writeValueAsString(myData));
response.flushBuffer(); // marks response as committed -- if we don't do this the request will go through normally!
}
you can see here why you have to flush the response. You can also do sendError HttpServletResponse#sendError How to change ContentType
If you don't flush the response, your request will continue down the filter chain (you have to add the chain.doFilter(request, response); of course!).
I am not sure with that but I think you can try this :
HttpServletResponse res = (HttpServletResponse) response;
ContentCachingResponseWrapper ccrw= new ContentCachingResponseWrapper(res);
//old body:
String content=new String(ccrw.getContentAsByteArray(), "utf-8");
//try this
HttpServletResponseWrapper hsrw=new HttpServletResponseWrapper(res);
hsrw.getOutputStream().write(/*new body*/);
//OR this
ServletResponseWrapper responseWrapper = (ServletResponseWrapper)response;
responseWrapper.getResponse().resetBuffer();
responseWrapper.getResponse().getOutputStream().write(/*new body*/);

How to set http status code when responding to servlet client from Filter class-method in tomcat

I am writing a webservice with spring (this question is not about spring...) that implements a (hopefully) restful api. From my understanding all response should be in xml or json format. This is not really a big deal in most cases. But in one situation this seems not possible. I am using a facility from tomcat where a servlet is involved. I have to use a filter for some reason (and this reason is authentication). As I am new to servlets my understanding is eventually not so well, but to me it looks like this
My filter-class derives from javax.servlet.filter and I am writing my code within the doFilter method:
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { // ... }
And at some point i realize i have to respond to the client with http status code 401 and also want to give him a xml or json information about what happened. Now to me it looks as if i can either
1) Use the ServletResponse: This allows me to get an OutputStream and write my xml/json out. However I cannot set the http status code at all. The final response arriving at the client does contain some http headers.
2) Cast ServletResponse to HttpServletResponse: This allows me to set status code, but I don't seem to be able to set the response body, but let response body be handled from tomcat.
Either way seems incomplete. If i use ServletResponse to write to the OutputStream and then cast to HttpServletResponse and then call sendError(401) - hoping that whatever I wrote to OutputStream reaches the client - my response does not contain an http "status line". However http headers are present like "Server: Apache-Coyote/1.1"
any help welcome...
I've implemented a filter for authentication shortly. I've coded something similar to this:
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain)
{
HttpServletResponse response=(HttpServletResponse) resp;
boolean authenticated=false;
// perform authentication
if (authenticated)
{
chain.doFilter(req, response);
}
else
{
// don't continue the chain
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("WWW-Authenticate", "BASIC realm=\"Your realm\"");
response.setContentType("what you need");
PrintWriter writer=response.getWriter();
// don't set content length , don't close
}
}
This works for me:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
response.resetBuffer();
response.getOutputStream().write("Your content".getBytes());
HttpServletResponse hsr = (HttpServletResponse) response;
hsr.setStatus(401);
chain.doFilter(request, response);
}
Method HttpServletResponse::sendError is working for me to return from filter in SpringBoot 2.0 application:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (authenticated)
{
chain.doFilter(req, response);
}
else
{
((HttpServletResponse) response).sendError(HttpStatus.FORBIDDEN.value(), "Authorization shall be provided");
}
chain.doFilter(request, response);
}

Disable filter based on response

I have a client caching disabling filter which is mapped to all URLs.
Now I have to specifically disable the filter and allow caching when the response has anything other than html.
My current filter code is as follows:
public class NoCacheFilter implements Filter{
#Override
public void destroy() {}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Pragma", "no-cache");
httpResponse.setHeader("Cache-Control","no-cache,no-store,max-age=0,s-maxage=0,must-revalidate,proxy-revalidate,private,max-stale=0,post-check=0");
httpResponse.setDateHeader("Expires", 0L);
filterChain.doFilter(request, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
NOTE: Just for people who want to know why I am doing this - We have some paged which generate PDF. We take this PDF File and flush to the output stream. In IE8, the flush code doesn't work and logs show that the client closed the connetion immaturely...Cannot write to committed response.... When caching is enabled, PDF get written to client normally; without any issue. A separate requirement of the existing app is to not allow caching of any page on the client.
Did you try something as simple as this:
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
filterChain.doFilter(request, response);
if(response.getContentType().indexOf("text/html")>-1){
httpResponse.setHeader("Pragma", "no-cache");
httpResponse.setHeader("Cache-Control","no-cache,no-store,max-age=0,s-maxage=0,must-revalidate,proxy-revalidate,private,max-stale=0,post-check=0");
httpResponse.setDateHeader("Expires", 0L);
}
}
It don't really disable the filter, but at least the filter don't do anything when the response is not html.
Simple, just check in the doFilter() method whether the resource is cacheable or not. You can use the request URI, content-type or MIME type for that.

How to apply a servlet filter only to requests with HTTP POST method

In my application I want to apply a filter, but I don't want all the requests to have to go to that filter.
It will be a performance issue, because already we have some other filters.
I want my filter to apply only for HTTP POST requests. Is there any way?
There is no readily available feature for this. A Filter has no overhead in applying to all HTTP methods. But, if you have some logic inside the Filter code which has overhead, you should not be applying that logic to unwanted HTTP methods.
Here is the sample code:
public class HttpMethodFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
if(httpRequest.getMethod().equalsIgnoreCase("POST")){
}
filterChain.doFilter(request, response);
}
public void destroy()
{
}
}

Differences between ServletResponse and HttpServletResponseWrapper?

I am new to servlet and reading some text about filters and wrappers. I can understand filters but got confused about wrappers. In the book, the author gives an example:
In case no wrapper:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String name = request.getParameter("name").trim();
try {
chain.doFilter(request, response);
PrintWriter out = response.getWriter();
if (name.length() == 0) {
out.println("Some message");
out.println("</body>");
out.println("</html>");
out.close();
}
} catch (Throwable t) {
}
}
In case of wrapper:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String name = request.getParameter("name").trim();
HttpServletResponse httpRes = (HttpServletResponse) response;
HttpServletResponseWrapper resWrapper = new HttpServletResponseWrapper(httpRes);
try {
chain.doFilter(request, response);
PrintWriter out = resWrapper.getWriter(); // why dont we just use response.getWriter();
if (name.length() == 0) {
out.println("<h3>Some message");
out.println("</body>");
out.println("</html>");
out.close();
}
} catch (Throwable t) {
}
}
Why we need HttpServletResponseWrapper while we can do the same thing with ServletResponse in case 1? Can anyone give me a clear example that we MUST use HttpServletResponseWrapper instead of ServletResponse? I have tried to google but found no luck.
BalusC's answer is good, but it might be a little overwhelming if you're just starting out.
Put simply: SerlvetResponse and its extension, HttpServletResponse, are interfaces telling you what methods are available to call to do the things you need. In the normal course of working with Filters, Servlets, et al., you'll use HttpServletResponse often to tell your app how to respond to requests.
HttpServletResponseWrapper is one particular implementation of HttpServletResponse which gives you a convenient way to wrap an existing response with some logic of your own without having to write a whole new implementation of the interface. It has a lot of methods, so this is really nice. As a trivial example, suppose you wanted to disallow calls to response.flushBuffer(). This code, using HttpServletResponseWrapper, will do that:
class DisallowFlushResponseWrapper extends HttpServletResponseWrapper {
public DisallowFlushResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void flushBuffer() {
throw new UnsupportedOperationException("Don't call this!");
}
}
The typical way to use such a wrapper would be to create a filter like this:
class DisallowFlushFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
if (response instanceof HttpServletResponse) {
HttpServletResponse newResponse =
new DisallowFlushResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, newResponse);
}
...
}
...
}
Note that we wrap the response coming into the filter with an instance of our own wrapper. Then we hand the wrapper down to the next item in the filter chain. Thus anything that comes after this filter will get an exception if it calls flushBuffer() because it will be calling it on our wrapper. The wrapper, due to its default behavior, will delegate any other call to the wrapped response, which is the real one, so everything except calls to that one method will work normally.
That's really a stupid example which does not show the benefit of request/response wrapper. Actually, the whole filter example is poor. Emitting HTML should be done by a JSP or at highest a servlet (but also that is still poor). Go through our filters wiki page to get some ideas about what a filter can be used for.
A response wrapper is useful if you want to modify the response's behaviour or just want to collect information about the response while it is been used in the request-response chain. The modified behaviour takes then place whenever some servlet or JSP calls a certain method on the response. If you have overriden it in your wrapper class, then this one will be called instead. You could alter the behaviour or collect information there.
Here on Stackoverflow you can find some concrete examples of useful HttpServletResponseWrapper implementations.
How to insert response size and time into the page itself, at least partially?
MD5 Signing a HttpServletResponse
How to configure Tomcat to not encode the session id into the URL when HttpServletResponse.encodeURL() is invoked
How to add response headers based on Content-type; getting Content-type before the response is committed
How is annotations support in jsp implemented in sitebricks?
Capture and log the response body
Log only http servlet response headers
How to include a JSP page in a Facelets page?
Capture generated dynamic content at server side

Categories

Resources