Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding custom header to Request via Filter

I have a class which extend Filter class and it looks like:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        System.out.println("--------------------------------------");

        HttpServletRequest request = (HttpServletRequest) req;

        req.setAttribute("test", "test");


        final HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        response.setHeader("Access-Control-Max-Age", "3600");
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

The question is can I add a custom header to request? I went through all method which are available inside request and req and couldn't find any way. But in debug mode I found that inside CoyoteRequest is list of headers. How can I add there my custom header?

enter image description here

like image 780
Jan Testowy Avatar asked Jan 13 '18 13:01

Jan Testowy


4 Answers

You can not set the headers in HttpServletRequestobject, but you can use a wrapper class.

Look at this guide: http://wilddiary.com/adding-custom-headers-java-httpservletrequest/


Just in case for future the link becomes invalid:

final class MutableHttpServletRequest extends HttpServletRequestWrapper {
    // holds custom header and value mapping
    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request){
        super(request);
        this.customHeaders = new HashMap<String, String>();
    }

    public void putHeader(String name, String value){
        this.customHeaders.put(name, value);
    }

    public String getHeader(String name) {
        // check the custom headers first
        String headerValue = customHeaders.get(name);

        if (headerValue != null){
            return headerValue;
        }
        // else return from into the original wrapped object
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    public Enumeration<String> getHeaderNames() {
        // create a set of the custom header names
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        // now add the headers from the wrapped request object
        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }

        // create an enumeration from the set and return
        return Collections.enumeration(set);
    }
}

usage:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(req);
    ...
    mutableRequest.putHeader("x-custom-header", "custom value");
    chain.doFilter(mutableRequest, response);
}
like image 61
Guy Smorodinsky Avatar answered Oct 20 '22 16:10

Guy Smorodinsky


I followed Guy Smorodinsky's answer and it worked for me but I had to add another method to the MutableHttpServletRequest:

    @Override
    public Enumeration<String> getHeaders(String name) {
        Set<String> set = new HashSet<>();
        Optional.ofNullable(customHeaders.get(name)).ifPresent(h -> set.add(h));
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaders(name);
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }
        Optional.ofNullable(customHeaders.get(name)).ifPresent(h -> set.add(h));
        return Collections.enumeration(set);
    }
like image 41
RogerParkinson Avatar answered Oct 20 '22 17:10

RogerParkinson


Guy Smorodinsky's answer is ok but for example for Spring you might want to override some additional methods, like Enumeration<String> getHeaders(String name), as Spring uses this while getting header value with @RequestHeader annotation.

Example code may look something like this:

  @Override
  public Enumeration<String> getHeaders(String name) {
    Set<String> headerValues = new HashSet<>();
    headerValues.add(this.headers.get(name));

    Enumeration<String> underlyingHeaderValues = ((HttpServletRequest) getRequest()).getHeaders(name);
    while (underlyingHeaderValues.hasMoreElements()) {
      headerValues.add(underlyingHeaderValues.nextElement());
    }

    return Collections.enumeration(headerValues);
  }
like image 8
Jakub Kvba Avatar answered Oct 20 '22 16:10

Jakub Kvba


If for any reason adding new filter or interceptor is not an option, and reflection and ugly code is OK. You can do the following to modify existing header programmatically in the controller method's body:

Warning! This is ugly and definitely shouldn't be used in production!

private void modifyHeaders(HttpServletRequest request, String headerToFind, String valueToReplace) {
    Field field = request.getHeaderNames().getClass().getDeclaredField("val$names");
    field.setAccessible(true);
    Field field2 = field.get(request.getHeaderNames()).getClass().getDeclaredField("headers");
    field2.setAccessible(true);
    Field field3 = field2.get(field.get(request.getHeaderNames())).getClass().getDeclaredField("headers");
    field3.setAccessible(true);

    Object objects = field3.get(field2.get(field.get(request.getHeaderNames())));
    
    List array = new ArrayList();

    int length = Array.getLength(objects);
    for (int i = 0; i < length; i++) {
        array.add(Array.get(objects, i));
    }

    for (Object obj : array.toArray()) {
        field = obj.getClass().getDeclaredField("nameB");
        field.setAccessible(true);

        Object headerKey = field.get(obj);

        if (headerKey.toString().equalsIgnoreCase(headerToFind)) {
            field = obj.getClass().getDeclaredField("valueB");
            field.setAccessible(true);

            MessageBytes messageBytes = MessageBytes.newInstance();
            messageBytes.setString(valueToReplace));

            field.set(obj, messageBytes);

            break;
        }
    }
}
like image 1
Mikhail Kholodkov Avatar answered Oct 20 '22 16:10

Mikhail Kholodkov