【问题标题】:Java filter failing to set response headersJava 过滤器未能设置响应标头
【发布时间】:2012-11-07 19:54:18
【问题描述】:

我正在尝试创建一个 Java“过滤器”,它检测自定义 HTTP 请求标头,并插入响应标头,以便文件自动下载。对此最重要的响应标头是“Content-Type = Attachment”响应标头。我创建了一个插入自定义 Header 的 HTTP 请求对象:

function myHttpObject(filePath){
function makeHttpObject() {
    return new XMLHttpRequest();
}

var request = makeHttpObject();

request.open("GET", filePath, false);
request.setRequestHeader("X-Wria-Download", "PDFdownload");
request.send(null);
window.open(filePath);
console.log(request.getAllResponseHeaders());
}

这会将 X-Wria-Download 标头插入到请求中。 然后我有一个 Java 过滤器,它会查找该请求标头并将响应标头设置为“Content-Type=attachment”

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Contenttypefilter implements Filter  {

protected FilterConfig filterConfig;

public void init(FilterConfig filterConfig) throws ServletException {
    this.filterConfig = filterConfig;
}

public void destroy() {
    //noop
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

    //get the headers we placed in the request
    //based on those request headers, set some response headers

    if(req.getHeader("X-Wria-Download") != null){
        res.setHeader("Content-Type", "application/pdf");
        res.setHeader("Content-Disposition", "attachment; filename=success.pdf");
    }

    chain.doFilter(req,res);
}


}

当然,web.xml 包含在所有 jsp 文件中包含过滤器的代码。

让我感到困惑的是,响应文件中设置了标头,但它没有按应有的方式下载。如果我把 res.setHeader("Content-Disposition", "attachment; filename=success.pdf");在“if”语句之外的行,那么它将起作用,但它将下载行为应用于我不想要的所有 JSP。

当我在 if 语句中有 res.setHeader 时,为什么它应用了内容处置但不起作用;然后在 if 语句之外工作?关于如何获得所需行为的任​​何想法(仅将内容处置应用于我已应用自定义请求标头的 jsp)?

【问题讨论】:

  • 您确定 X-Wria-Download 在 Java 中被正确接收吗?你可以尝试打印 req.getHeader("X-Wria-Download") 并查看它是否真的在 Java 过滤器中正确接收?
  • 是的,它正在正确接收它。在上面的“if”语句中: if(req.getHeader("X-Wria-Download") != null){} 它进入语句,如果我输入 System.out.println 它将打印出该行。我还将 Content-Disposition 应用于文件,但 Content-Type 似乎在 chain.Filter 阶段被覆盖。尽管响应获得了 Content-Disposition,但它的行为似乎并不像它那样(不下载,而是将 pdf 作为页面拉出。)我还看到了“X-Wria-Download”使用 Chrome 开发者工具时的请求头。
  • 奇怪的是:res.setHeader("Content-Type", "application/pdf"); res.setHeader("Content-Disposition", "attachment; filename=success.pdf");放在 if 语句之外,则过滤器将起作用。这并不能解决我的问题,因为我只希望将过滤器应用于已附加自定义标头的 servlet。
  • 向我的 cmets 添加了一个关于此的答案。

标签: java servlets http-headers request servlet-filters


【解决方案1】:

试试这个:如果请求标头存在,则在请求上设置一个属性。然后,检查chain.doFilter(...)之后的属性,然后设置响应头。

【讨论】:

  • 好的,我试过这样做。出于某种原因,在 chain.doFilter 之后,内容类型总是恢复为 text/html。
【解决方案2】:

问题是在提供过滤器之前,您的 AjaxRequest(此处为 XMLHttpRequest)的标头(X-Wria-Download)未在您的 HttpServletRequest 对象中设置。

我认为更好的想法是使用专用 Servlet 来处理您的 ajax 请求

【讨论】:

  • 标头(X-Wria-Download)实际上似乎在被传递给java过滤器之前被应用于请求对象。在调试模式下通过过滤器时,它会检测到自定义请求标头,然后执行上面的 if 语句:if(req.getHeader("X-Wria-Download") != null){}。
【解决方案3】:

我认为您的问题与您的 Web Context 的过滤器执行顺序有关,即在您的网络上下文中,某些过滤器在您的过滤器之后执行并覆盖标题。

Servlet FilterChain of Responsibility 模式的实现

所以你可以尝试:

  • 调用chain.doFilter后设置headers:

.

...

chain.doFilter(req,res);

HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;

//get the headers we placed in the request
//based on those request headers, set some response headers

if(req.getHeader("X-Wria-Download") != null){
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", "attachment; filename=success.pdf");
}

这样,您的代码将在调用 Servlet 后执行,如下所述,如果您的过滤器是 web.xml 中第一个声明的,那么 setHeader 代码将是最后执行的(见下图)。

  • 确保您的过滤器是 Servlet 执行后最后执行的,即它应该是第一个声明为 here 解释的 servlet 过滤器:

如您所见,Filter1(web.xml 中第一个声明的)是在 servlet 执行之前执行的第一个,也是在 servlet 执行之后执行的最后一个。因此,如果您想确保成为最后一个 Filter 设置 header 则将其声明为 Filter1。

执行顺序由您的部署描述符(web.xml)中的声明顺序决定:

Servlet 规范(第 6.2.4 节):

"容器在构建过滤器链时使用的顺序是 申请特定请求的URI如下:

"1.首先,匹配的过滤器映射在同一个 以便这些元素出现在部署描述符中。

"2.接下来,匹配过滤器映射在同一个 以便这些元素出现在部署描述符中。”

因此,只需将其声明为web.xml 中的第一个过滤器即可。这样,它将是设置标题的最后一个过滤器。当然,如前所述,在调用 chain.doFilter 之后在代码中设置标头。

【讨论】:

  • 谢谢!对我有帮助的关键是 1) 确保首先添加您的过滤器,以便最后调用它,以及 2) 确保修改响应 after 调用 chain.doFilter(req ,res),而不是之前,否则您的回复可能会被覆盖。
  • 不幸的是,这对我使用 Dropwizard 0.6 / Jetty 8 不起作用。(我正在尝试修改 cookie 值。)出于某种原因,无论我在哪里添加 chain.doFilter() 调用,我的响应返回标头的原始值。我不得不以一种不同的、不太理想的方式解决我的问题,但我只是对这个答案发表评论,以防其他人也有问题。
  • 感谢您的精彩描述。我试过用 Jetty 9.2 做这样的过滤器,但不幸的是它也没有工作,似乎和@DuffJ 有同样的问题。在我的情况下,只有当我在 web.xml 中将其声明为最后一个过滤器时,过滤器才能按预期工作。
【解决方案4】:

假设您使用其他人在此处描述的响应包装器,那么整个秘密就是何时在原始响应上调用 getWriter() !那是因为响应对象会忽略在您要求作者之后添加的所有标头!

因此,请确保在调用 getWriter() 之前添加所有标题。 这是我推荐的 doFilter() 顺序:

  1. 创建响应包装器

  2. chain.doFilter(origRequest,包装器);

  3. 将所有必需的标头分配给原始 (!) 响应

  4. 从原始响应中获取作者

  5. 将包装器的内容复制到此作者

【讨论】:

    猜你喜欢
    • 2012-03-30
    • 2018-06-24
    • 2011-04-26
    • 1970-01-01
    • 2017-06-25
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多