Servlet 过滤器:如何从 servletRequest 中获取所有的头部信息?

97

这是我的WebFilter的样子。

@WebFilter("/rest/*")
public class AuthTokenValidatorFilter implements Filter {

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
        final Enumeration<String> attributeNames = servletRequest.getAttributeNames();
        while (attributeNames.hasMoreElements()) {
            System.out.println("{attribute} " + servletRequest.getParameter(attributeNames.nextElement()));
        }

        final Enumeration<String> parameterNames = servletRequest.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            System.out.println("{parameter} " + servletRequest.getParameter(parameterNames.nextElement()));
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
    }
}

我试图在网上找出如何获取来自请求的HTTP头信息的方法。

我没有找到任何有用的信息,所以我尝试使用 servletRequest.getAttributeNames()servletRequest.getParameterNames() 枚举,但我没有获得任何头信息。

问题:
如何获取来自请求的所有头信息?

4个回答

127

ServletRequest 强制转换为 HttpServletRequest (仅当 ServletRequest requestHttpServletRequest 的实例时)。

然后您可以使用 HttpServletRequest.getHeader()HttpServletRequest.getHeaderNames() 方法。

像这样:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    Enumeration<String> headerNames = httpRequest.getHeaderNames();

    if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                    System.out.println("Header: " + httpRequest.getHeader(headerNames.nextElement()));
            }
    }

    //doFilter
    chain.doFilter(httpRequest, response);
}

@BuhakeSindi 有没有迹象表明 ServletRequest 在什么情况下可能与 HttpServletRequest 不同? - sthzg
1
'HttpServletReqiest'是'ServletRequest'的子接口。ServletRequest可以有各种类/接口,如'PortletRequest',因此始终要对各种servlet类型进行'instanceof'检查。 - Buhake Sindi
为什么 getHeaderNames(); 方法会漏掉像 x-forward-for、keep-alive 等一些头部信息呢? - Asif Mushtaq
1
@UnKnown,这些头信息是由代理服务器填充的。它必须在服务器级别上进行配置。Servlet只返回这些服务器设置的参数。 - Buhake Sindi

81

使用Java 8+,你可以使用流来收集请求头:

HttpServletRequest httpRequest = (HttpServletRequest) request;

Map<String, String> headers = Collections.list(httpRequest.getHeaderNames())
    .stream()
    .collect(Collectors.toMap(h -> h, httpRequest::getHeader));

更新

@Matthias提醒我,标头可以具有多个值:

Map<String,List<String>>

Map<String, List<String>> headersMap = Collections.list(httpRequest.getHeaderNames())    
    .stream()
    .collect(Collectors.toMap(
        Function.identity(), 
        h -> Collections.list(httpRequest.getHeaders(h))
    ));

org.springframework.http.HttpHeaders

:这是Spring框架中的一个类,用于表示HTTP请求或响应的头信息。
HttpHeaders httpHeaders = Collections.list(httpRequest.getHeaderNames())
    .stream()
    .collect(Collectors.toMap(
        Function.identity(),
        h -> Collections.list(httpRequest.getHeaders(h)),
        (oldValue, newValue) -> newValue,
        HttpHeaders::new
    ));

https://gist.github.com/Cepr0/fd5d9459f17da13b29126cf313328fe3


1
你尝试编译过这个代码吗? - Eugene
我不得不稍微调整一下它以适应Spring - response.getHeaderNames() .stream() .collect(Collectors.toMap( Function.identity(), h -> new ArrayList<>(response.getHeaders(h)), (oldValue, newValue) -> newValue, HttpHeaders::new )); - bobmarksie
@bobmarksie 在你的变量中,你使用了 response,但问题是关于 request 的。不幸的是,HttpServletRequest#getHeaderNames 返回的是 Enumeration,而不是 Collection - Cepr0
谢谢@Cepr0!即使我使用它的方式不同,我仍然发现你的答案非常有用。 - bobmarksie
特别是gist网址非常有用,可以用来测试并澄清所有疑问。 - Paramesh Korrakuti

5

请注意,同一个HTTP头可以出现多次,且值可能不同:

Map<String, Serializable> headers = Collections.list(request.getHeaderNames()).stream().collect(Collectors.toMap(h -> h, h -> {
    ArrayList<String> headerValues = Collections.list(request.getHeaders(h));
    return headerValues.size() == 1 ? headerValues.get(0) : headerValues;
}));

请不要在Serializable中混合使用StringArrayList。当消费Map时,我需要再次使用instanceof来检查值。 - Alexander Dümont

1
在Spring Boot应用程序中,这适用于任何方法(使用Java 8)。
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import javax.servlet.http.HttpServletRequest;
    import java.util.Collections;
    
...
...

    public void anyMethod(){
        HttpServletRequest req = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        Collections.list(req.getHeaderNames()).stream().forEach(System.out::println);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接