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.
Related
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*/);
I create an function to create a session expiry date, like below:
#Override
public Long sessionTime() throws ErrorException{
Long expirySession = System.currentTimeMillis() + expirationTime;
return expirySession;
}
she is launch after every request and save this time in database,
but i must to send this time in all headers from my request in controller,
can somebody tell me how can i do this?
You can use the Response entity headers
#GetMapping("/response-entity")
public ResponseEntity<String> usingResponseEntityBuilderAndHttpHeaders() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("sessionTime" ,sessionTime());
return ResponseEntity.ok()
.headers(responseHeaders)
.body("ResponseEntity");
}
For more details check out https://www.baeldung.com/spring-response-header
You can do that in Filter to have a generic handler for that feature in one place.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/package-summary.html
You need to implement doFilter method in this way:
public class Example implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response .addHeader("header_name", "header_value");
filterChain.doFilter(servletRequest, response);
}
}
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);
}
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.
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()
{
}
}