I'm reading some tutorial where before invocating any action there is a filter that sets an attribute in the ServletRequest as Connection.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Connection connection = new ConnectionFactory().getConnection();
request.setAttribute("connection", connection);
chain.doFilter(request, response);
connection.close();
}
However I still didn't find a way to get the attribute in my Action. How can I get it?
Map parameters = ActionContext.getContext().getParameters();
Another option is that your action class implements ServletRequestAware. In the implementation of the method you simply assign the request to an instance field.
Related
I am new to Servlets. In the book i am reading now it is written, that we need wrappers, because it is late to do anything with response after finishing chain.doFilter() method as response is sent already.
I wrote the following Servlet and Filter:
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
PrintWriter writer = response.getWriter();
writer.println("In Servlet");
}
}
public class MyFilter implements Filter{
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException{
PrintWriter writer = response.getWriter();
chain.doFilter(request, response);
writer.println("After chain");
}
}
And i see both strings in the browser.
My question is: Why do we need wrappers? I still can write to response even after chain.doFilter and i still see result?
Is it because response is sent in two pieces(first in the end of chain.doFilter and second in the end of Filter.doFilter)? So if i had to compress response it would work incorrectly(because first uncompressed part would be sent)?
The book is talking about response headers.
You misunderstood it as response body.
Here are some real world use cases of response wrappers so you can see why we may need them:
How to add response headers based on Content-type; getting Content-type before the response is committed
How do delete a HTTP response header?
How to read and copy the HTTP servlet response output stream content for logging
How to insert JSF page rendering time and response size into the page itself, at least partially?
How to configure Tomcat to not encode the session id into the URL when HttpServletResponse.encodeURL() is invoked
For more examples, see this search.
My understanding of filters is that they can be used only for preprocessing, and once their task is finished, they give control to the invoked resource, and then control never gets back to the filter. So they can never be used for post-processing?
No, you're not right. A filter method that needs to pre-process and post-process typically looks like:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
preProcess(request, response);
chain.doFilter(request, response);
postProcess(request, response;
}
I'm trying to add an Attribute to the request via doFilter before passing it to the Servlet, so that a value in JSTL will be set. I simply do req.setAttribute("b", "blah") in the filter, but it doesn't seem to get set in the JSTL file. How would I do this?
It's hard to pinpoint the root cause without seeing the code. There are several possible causes.
You're sending a redirect after setting the attribute instead of continuing with the same request.
You're accessing the attribute with the wrong name (case sensitive!).
You're accessing the attribute the wrong way.
The attribute is been overridden somewhere further down in the request processing.
There's a page scoped attribute with the same name which has no value.
You're misinterpreting the results.
Etc.
By the way, there's no such thing as a "JSTL file". Perhaps you meant "JSP file".
The problem is that the doFilter method uses ServletRequest instead of HttpServletRequest which is the one that has the setAttribute method. Most of my filters are something like this:
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// do what you must...
chain.doFilter(servletRequest, servletResponse);
}
I am trying to implement a filter that uses MockHttpServletRequest to add a header to the request object. I want to use that header for preauthentication. This is the filter class..
public class MockAuthFilter implements Filter{
private FilterConfig filterConfig = null;
private static String loggedInUserName = "myId";
private static String httpRequestHeaderName = "SM_ID";
private Logger logger = Logger.getLogger(MockAuthFilter.class);
#Override
public void destroy() {
this.filterConfig = null;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(this.filterConfig.getServletContext() == null){}
HttpServletRequest httpRequest = (HttpServletRequest) request;
MockHttpServletRequest mRequest = new MockHttpServletRequest(this.filterConfig.getServletContext());
if(mRequest.getHeader(httpRequestHeaderName)==null ||
!mRequest.getHeader(httpRequestHeaderName).equalsIgnoreCase(loggedInUserName))
mRequest.addHeader(httpRequestHeaderName, loggedInUserName);
mRequest.setMethod("GET");
mRequest.setRequestURI(httpRequest.getRequestURL().toString());
logger.debug("**********************exiting doFilter() method*****************");
chain.doFilter(mRequest, response);
}
#Override
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
}
but there is not url populated when the request reaches the next filter in the filter chain of Spring Security. I see following lines in the log file..
[2011-10-13 16:52:35,114] [DEBUG] [http-8080-1] [com.app.filter.MockAuthFilter:doFilter:55] - **********************exiting doFilter() method*****************
[2011-10-13 16:52:35,114] [DEBUG] [http-8080-1] [com.app.filter.MockAuthFilter:doFilter:55] - **********************exiting doFilter() method*****************
[2011-10-13 16:52:35,114] [DEBUG] [http-8080-1] [org.springframework.security.web.util.AntPathRequestMatcher:matches:103] - Checking match of request : ''; against '/static/**'
[2011-10-13 16:52:35,114] [DEBUG] [http-8080-1][org.springframework.security.web.util.AntPathRequestMatcher:matches:103] - Checking match of request : ''; against '/static/**'
As you can see there was no url in the request object passed onto AntPathrequestMatcher's matches method..
I have checked mockrequest object right before chain.doFilter() method and it contains the url value in its requestURI field. if URI and URL aren't the same thing here, what changes should I make here so that url is maintained in the request object..
Don't use MockHttpServletRequest like that. It's for testing, not for production code. The appropriate way to wrap a request and/or response in a filter to modify its behavior later on is with HttpServletRequestWrapper and HttpServletResponseWrapper.
Why are you even trying to wrap or remove the original request if all you want is to add a header? Just use addHeader() on the incoming request.
Trying to change the request method and request URI in the filter as you're doing may have unexpected consequences and will almost certainly not do what you probably expect it to do. By the time the request hits your filter, the filter chain has already been built based on the original state of the request. Changing it now won't affect where it ends up going.
I have a situation where one of the response headers Content-Disposition has to be removed. So I thought of writing a servlet filter to do this. But I realized that the HttpServletResponse has only a setHeader() method but no method to remove it.
How can I do this?
You can't delete headers afterwards by the standard Servlet API. Your best bet is to just prevent the header from being set. You can do this by creating a Filter which replaces the ServletResponse with a custom HttpServletResponseWrapper implementation which skips the setHeader()'s job whenever the header name is Content-Disposition.
Basically:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("Content-Disposition")) {
super.setHeader(name, value);
}
}
});
}
Just map that filter on the URL-pattern of interest to get it to run.
This may not be Servlet API compliant, but setting the value to null works on GlassFish 4 and probably on Tomcat too as that is what is underneath GlassFish.
We really need to update the Servlet API specification to either add a method to allow removing headers or to officially support using setHeader with a null value.
An example where this is important is if you use a security constraint (SSL/TLS) on your web application then static resource caching is complicated by the fact that the container will automatically add headers to prevent caching (you can try to disable with disableProxyCaching and securePagesWithPragma on Tomcat/GlassFish). I've already got a servlet filter for cache control that works great for non-secure content so I would like to keep cache control all in one place and simply set Prama and Cache-Control to null to clear any container added headers.
As the other responses. There is no way to remove a header after being set, at least not standard (glassfish lets clear a header setting it's value to null). So at the end of the day you would have two choices:
Reset the response with response.reset() - This effectively removes ALL headers AND ALSO ANY BUFFERED DATA, depending on you case can be a good alternative (in my case was after authentication validation errors). If the response is already committed you'll get an IllegalStateException.
Set header to empty string, clearly this doesn't remove the header. But the http specification only has some definitions for and empty value in the Accept-Encoding, TE (transfer encoding) and HOST headers, so depending in your needs you can control that in your application layer.
This does not work for me using Spring 4. I'm trying to strip out the Expires response header. For every page. Like so:
public class CachingFilter implements Filter {
private final Log logger = LogFactory.getLog(getClass());
public CachingFilter() {}
public void destroy() {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.debug("doFilter()");
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
logger.debug("setHeader(" + name + ","+value+")");
if (!name.equalsIgnoreCase("Expires")) {
super.setHeader(name, value);
}
}
});
}
public void init(FilterConfig fConfig) throws ServletException {}
}
And here is how I add the filter:
public class AppConfig implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppContext.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
FilterRegistration.Dynamic noCache = servletContext.addFilter("noCacheFilter", new CachingFilter());
noCache.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
servletContext.addListener(new ContextLoaderListener(rootContext));
}
}
setHeader() being called for Expires and Cache-Control, but I can't override the Expires filter value, or the Cache-Control value. I can add to the Cache-Control value. It turns into an array of values if I call setHeader on Cache-Control. But I need to delete the header.