考虑以下代码向客户端发送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规范states:
14.30位置
Location response-header字段用于将收件人重定向到Request-URI以外的位置,以完成请求或标识新资源。对于201(已创建)响应,Location是请求创建的新资源的位置。对于3xx响应,位置应该应该指示服务器自动重定向到资源的首选URI。字段值由单个绝对URI组成。
Location = "Location" ":" absoluteURI
一个例子是:
Location: http://www.w3.org/pub/WWW/People.html
我的问题是如何以适当的servlet方式将相对URL转换为Location头的绝对URL。
我不相信使用:
request.getServerName() + ":" + request.getServerPort() + url;
是正确的解决方案吗?应该有一个生成正确输出的标准方法(以便可以应用URL重写等)。我不想创建黑客。
答案 0 :(得分:22)
只需发送绝对路径即可。 对绝对URI的限制是RFC 2616中的已知缺陷,将在HTTPbis中修复(参见http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185)。
请注意,RFC 7231 现在包含the spec中的相对URI。请参阅有关如何处理相对URI的其他答案。
答案 1 :(得分:4)
不幸的是,servlet API没有提供一个直接返回绝对URL的方法,该方法与上下文根一致。为此,我有几次不得不使用getRequestURL()
,getRequestURI()
和getContextPath()
的组合。
String absoluteContextRootURL = request.getRequestURL().toString().replace(request.getRequestURI().substring(1), request.getContextPath());
答案 2 :(得分: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”可以正常工作:))
现在接受朱利安的回答......
答案 3 :(得分:4)
如果您使用的是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。
的错误答案 4 :(得分:2)
您可以尝试
new URL(new URL(request.getRequestURL().toString()), url).toString();
至少可以将任何..
或其他奇怪的东西标准化。除此之外,我认为它不比字符串操作好得多。