【问题标题】:What's the proper way to set the Location header for an HTTP 201 response in a Java Servlet application在 Java Servlet 应用程序中为 HTTP 201 响应设置 Location 标头的正确方法是什么
【发布时间】:2011-06-29 22:58:28
【问题描述】:

考虑以下代码向客户端发送 HTTP 201“已创建”响应:

    String url = "/app/things?id=42"; // example
    response.setStatus(HttpServletResponse.SC_CREATED);
    response.setContentType("text/plain");
    response.setHeader("Location", url);
    response.getWriter().print(url);

它通知客户端创建了一个新的“事物”,并且可以在 URL /app/things?id=42 找到它。问题是这个 URL 是相对的。这对于 JSP 来说是完美的,它可以写成如下:

<img src="<c:url value="/things?id=42" />" />

这将产生以下 HTML:

<img src="/app/things?id=42" />

这就是我们想要的网络应用程序。

但我不认为这是我们想要的 201 响应 Location 标头。 HTTP 规范states:

14.30 地点

Location 响应头字段用于将接收者重定向到 Request-URI 以外的位置,以完成请求或识别新资源。对于 201(已创建)响应,位置是请求创建的新资源的位置。对于 3xx 响应,位置应该指示服务器的首选 URI,用于自动重定向到资源。字段值由单个绝对 URI 组成。

       Location = "Location" ":" absoluteURI

一个例子是:

       Location: http://www.w3.org/pub/WWW/People.html

我的问题是如何以正确的方式将相对 URL 转换为 Location 标头的绝对 URL。

我不相信使用:

request.getServerName() + ":" + request.getServerPort() + url;

是正确的解决方案。应该有一个产生正确输出的标准方法(以便可以应用 URL 重写等)。我不想创建一个 hack。

【问题讨论】:

    标签: java http servlets http-headers httpresponse


    【解决方案1】:

    只发送绝对路径。 对绝对 URI 的限制是 RFC 2616 中的一个已知缺陷,将在 HTTPbis 中修复(请参阅http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185)。

    请注意,RFC 7231 现在在 the spec 中包含相对 URI。有关如何处理相对 URI,请参阅其他答案。

    【讨论】:

    • 请注意,RFC 7231 现在在规范中包含相对 URI,请参阅here“字段值由单个 URI 引用组成。当它具有相对引用的形式时 ( [RFC3986],第 4.2 节),通过根据有效请求 URI([RFC3986],第 5 节)解析它来计算最终值。”
    【解决方案2】:

    如果您使用 JAX RS,javax.ws.rs.core.Response 中有一个方法 automatically converts relative URLs:

    public static Response.ResponseBuilder created(java.net.URI location)

    为创建的资源创建一个新的 ResponseBuilder,使用提供的值设置位置标头。

    参数:

    • location - 新资源的 URI。如果提供了相对 URI,它将通过相对于请求 URI 解析它转换为绝对 URI。

    但请注意,JAX RS 实现 CXF 中存在一个错误,leads to incorrect absolute URLs

    【讨论】:

      【解决方案3】:

      不幸的是,servlet API 没有提供直接返回绝对 URL 直到上下文根的方法。为此,我不得不多次使用getRequestURL()getRequestURI()getContextPath() 的组合。

      String absoluteContextRootURL = request.getRequestURL().toString().replace(request.getRequestURI().substring(1), request.getContextPath());
      

      【讨论】:

        【解决方案4】:

        决定听从 Julian Reschke 的建议并违反规范!至少我添加了以下评论:

                /* Note: strictly speaking (per section 14.30 of RFC 2616), the Location header 
                 * requires an *absolute URI*. However, in practice, many web 
                 * applications send an *absolute path* instead. This is interoperable, 
                 * that is, works in popular web browsers  (according to 
                 * http://en.wikipedia.org/wiki/HTTP_location).
                 * 
                 * As the information required to set the Location header to an absolute URI
                 * is not generally available to a Servlet, we go with the flow and send
                 * an absolute path instead (in violation of RFC 2616).
                 * 
                 * There is an issue filed with Hypertext Transfer Protocol Bis (httpbis) 
                 * working group to resolve this problem here:
                 * http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185
                 */
                response.setHeader("Location", url);
        

        我不想自己发送绝对 URI 的原因是因为我在负载平衡器和其他生产基础设施后面看到了这个问题。虽然在开发模式下“http://localhost:8080/foo”往往可以正常工作:))

        现在会接受朱利安的回答...

        【讨论】:

        • 一旦 HTTPbis 规范发布,您将不再违反适用规范。
        【解决方案5】:

        你可以试试

        new URL(new URL(request.getRequestURL().toString()), url).toString();
        

        至少在规范化任何.. 或其他奇怪的东西方面会很聪明。除此之外,我不认为它比字符串操作更好。

        【讨论】:

          猜你喜欢
          • 2014-07-25
          • 1970-01-01
          • 2016-07-16
          • 2014-06-03
          • 2014-09-09
          • 1970-01-01
          • 2019-12-17
          • 1970-01-01
          • 2018-05-03
          相关资源
          最近更新 更多