在Java Servlet应用程序中,为HTTP 201响应设置Location头的正确方法是什么?

37
考虑以下发送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);

这告诉客户端一个新的"thing"被创建了,它可以在URL /app/things?id=42 找到。问题是这个URL是相对的。这对于JSP来说非常完美,可能会写成以下方式:

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

它会产生以下HTML:

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

这正是我们所期望的网络应用程序。

但是我不认为这是201响应位置标头所需的。HTTP规范说明

14.30 位置

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

       Location = "Location" ":" absoluteURI

一个例子是:

       Location: http://www.w3.org/pub/WWW/People.html
我想知道如何将相对 URL 转换为适用于 Servlets 中的 Location 头部的绝对 URL。
我不认为使用以下方法是正确的:
request.getServerName() + ":" + request.getServerPort() + url;

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

5个回答

21

4
请注意,RFC 7231现在将相对URI包括在规范中,详见此处:“该字段的值由单个URI参考组成。当它采用相对引用([RFC3986]第4.2节)形式时,最终值是通过与有效的请求URI解析([RFC3986]第5节)得到的。” - Claudiu

5
不幸的是,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”通常运行良好 :))

现在接受Julian的答案...


一旦HTTPbis规范发布,您将不再违反适用的规范。 - Julian Reschke

4
如果您正在使用JAX RS,则javax.ws.rs.core.Response中有一个方法可以自动将相对URL转换为绝对URL:created(java.net.URI location)。该方法用于为创建的资源创建新的ResponseBuilder,并使用提供的值设置位置标头。其中,参数location是新资源的URI,如果提供了相对URI,则它将通过将其相对于请求URI解析为绝对URI而进行转换。
需要注意的是,在JAX RS实现CXF中存在一个缺陷,导致生成的绝对URL不正确。CXF-5973

2
您可以尝试:
new URL(new URL(request.getRequestURL().toString()), url).toString();

这至少可以聪明地规范化掉任何..或其他奇怪的东西。除此之外,我认为它并没有比字符串操作好多少。


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