HttpServletRequestWrapper
HttpServletRequestWrapper
What is the purpose of wrapping an HttpServletRequest using an HttpServletRequestWrapper? Why do we wrap HttpServletRequest?
The HttpServletRequestWrapper class provides the ability to wrap and modify incoming HttpServletRequest objects.
HttpServletRequest
is an interface for a HTTP specific servlet request. Typically you get instances of this interface in servlet filters or servlets.
Sometimes you want to adjust the original request at some point. With a HttpServletRequestWrapper
you can wrap the original request and overwrite some methods so that it behaves slightly different.
Example 1
You have a bunch of servlets and JSPs which expect some request parameters in a certain format. E.g. dates in format yyyy-MM-dd
.
Now it is required to support the dates also in a different format, like dd.MM.yyyy
with the same functionality. Assuming there is no central string to date function (it’s an inherited legacy application), you have to find all places in the servlets and JSPs.
As an alternative you can implement a servlet filter. You map the filter so that all requests to your servlets and JSPs will go through this filter.
The filter’s purpose is to check the date parameters’ format and reformat them to the old format if necessary. The servlets and JSPs get the date fields always in the expected old format. No need to change them.
This is the skeleton of your filter:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest adjustedRequest = adjustParamDates((HttpServletRequest) request);
chain.doFilter(adjustedRequest, response);
}
We take the original request and in method adjustParamDates()
we manipulate the request and pass it down the filter chain.
Now, how would we implement adjustParamDates()
?
private HttpServletRequest adjustParamDates(HttpServletRequest req) {
// ???
}
We need a new instance of interface HttpServletRequest
which behaves exactly like the original instance req
. But the four methods getParameter()
, getParameterMap()
, getParameterNames()
, getParameterValues()
shouldn’t work on the original parameters but on the adjusted parameter set. All other methods of interface HttpServletRequest
should behave like the original methods.
So we can do something like that. We create an instance of HttpServletRequest
and implement all methods. Most method implementations are very simple by calling the corresponding method of the original request instance:
private HttpServletRequest adjustParamDates(final HttpServletRequest req) {
final Map<String, String[]> adjustedParams = reformatDates(req.getParameterMap());
return new HttpServletRequest() {
public boolean authenticate(HttpServletResponse response) {
return req.authenticate(response);
}
public String changeSessionId() {
return req.changeSessionId();
}
public String getContextPath() {
return req.getContextPath();
}
// Implement >50 other wrapper methods
// ...
// Now the methods with different behaviour:
public String getParameter(String name) {
return adjustedParams.get(name) == null ? null : adjustedParams.get(name)[0];
}
public Map<String, String[]> getParameterMap() {
return adjustedParams;
}
public Enumeration<String> getParameterNames() {
return Collections.enumeration(adjustedParams.keySet());
}
public String[] getParameterValues(String name) {
return adjustedParams.get(name);
}
});
}
There are more than 50 methods to implement. Most of them are only wrapper implementations to the original request. We need only four custom implementations. But we have to write down all these methods.
So here comes the class HttpServletRequestWrapper
into account. This is a default wrapper implementation which takes the original request instance and implements all methods of interface HttpServletRequest
as simple wrapper methods calling the corresponding method of the original request, just as we did above.
By subclassing HttpServletRequestWrapper
we only have to overwrite the four param methods with custom behaviour.
private HttpServletRequest adjustParamDates(final HttpServletRequest req) {
final Map<String, String[]> adjustedParams = reformatDates(req.getParameterMap());
return new HttpServletRequestWrapper(req) {
public String getParameter(String name) {
return adjustedParams.get(name) == null ? null : adjustedParams.get(name)[0];
}
public Map<String, String[]> getParameterMap() {
return adjustedParams;
}
public Enumeration<String> getParameterNames() {
return Collections.enumeration(adjustedParams.keySet());
}
public String[] getParameterValues(String name) {
return adjustedParams.get(name);
}
});
}
Example 2
Reference: https://stackoverflow.com/questions/2811769/adding-an-http-header-to-the-request-in-a-servlet-filter
Lets say that we are integrating with an existing servlet that pulls some properties out of the HTTP header. Basically, we are implementing an interface that doesn’t have access to the actual request, it just has access to a map of k->v for the HTTP headers. We need to pass in a request parameter. The plan is to use a servlet filter to go from parameter to header value but of course the HttpServletRequest object doesn’t have an addHeader()
method. How would we do it?
Extend HttpServletRequestWrapper, override the header getters to return the parameters as well:
public class AddParamsToHeader extends HttpServletRequestWrapper {
public AddParamsToHeader(HttpServletRequest request) {
super(request);
}
public String getHeader(String name) {
String header = super.getHeader(name);
return (header != null) ? header : super.getParameter(name); // Note: you can't use getParameterValues() here.
}
public Enumeration getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.addAll(Collections.list(super.getParameterNames()));
return Collections.enumeration(names);
}
}
..and wrap the original request with it:
chain.doFilter(new AddParamsToHeader((HttpServletRequest) request), response);
Write a ServletFilter to add the remote_addr header.
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
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;
/**
*
* @author wf
*
*/
public class RemoteAddrFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);
String remote_addr = request.getRemoteAddr();
requestWrapper.addHeader("remote_addr", remote_addr);
chain.doFilter(requestWrapper, response); // Goes to default servlet.
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// https://stackoverflow.com/questions/2811769/adding-an-http-header-to-the-request-in-a-servlet-filter
// http://sandeepmore.com/blog/2010/06/12/modifying-http-headers-using-java/
// http://bijubnair.blogspot.de/2008/12/adding-header-information-to-existing.html
/**
* allow adding additional header entries to a request
*
* @author wf
*
*/
public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {
/**
* construct a wrapper for this request
*
* @param request
*/
public HeaderMapRequestWrapper(HttpServletRequest request) {
super(request);
}
private Map<String, String> headerMap = new HashMap<String, String>();
/**
* add a header with given name and value
*
* @param name
* @param value
*/
public void addHeader(String name, String value) {
headerMap.put(name, value);
}
@Override
public String getHeader(String name) {
String headerValue = super.getHeader(name);
if (headerMap.containsKey(name)) {
headerValue = headerMap.get(name);
}
return headerValue;
}
/**
* get the Header names
*/
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
for (String name : headerMap.keySet()) {
names.add(name);
}
return Collections.enumeration(names);
}
@Override
public Enumeration<String> getHeaders(String name) {
List<String> values = Collections.list(super.getHeaders(name));
if (headerMap.containsKey(name)) {
values.add(headerMap.get(name));
}
return Collections.enumeration(values);
}
}
}
Sample implementation
https://github.com/explorer436/programming-playground/tree/main/java-playground/spring-http-demo