我有一个 Web 服务设置,可以从根目录 ('/') 重定向到页面 ('my/page.html')。
因此,http://localhost:8080/
应重定向到http://localhost:8080/my/page.html
。
这是代码:
@RequestMapping(method = RequestMethod.GET, value = "/")
public RedirectView localRedirect()
{
final RedirectView redirectView = new RedirectView();
redirectView.setContextRelative(true);
redirectView.setUrl("/my/page.html");
return redirectView;
}
我的期望是重定向响应将其 location
header 设置为相对路径:/my/page.html
。然而,实际上,它被设置为完整路径:http://localhost/my/page.html
。
这会导致一些问题,因为该应用程序运行在 Docker 容器中,该容器认为它正在为端口 80 提供服务;您可能已经注意到,完整路径在 URL 中删除了 :8080
端口说明符。 Docker 容器在反向代理后面运行,它将对 8080
的请求映射到容器。如果 location
header 是相对的并设置为 /my/page.html
,则期望浏览器客户端将使用正确的主机名 (localhost:8080
code>),因此它将被反向代理重定向到正确的页面。
正如您从我的代码中看到的,我尝试将 RedirectView
对象中的 ContextRelative
选项设置为 true。我还缺少其他东西吗?
编辑
@RequestMapping(method = RequestMethod.GET, value = "/")
public void localRedirect(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_FOUND);
response.setHeader("Location", "/my/page.html");
}
我已经使用上面的代码进行了重定向。但是,我仍然很好奇是否有人知道如何使用 Spring 的 RedirectView
和 RedirectStrategy
来完成上述任务,我很乐意接受该解决方案。
最佳答案
你的代码的行为是正确的,因为你说你的redirectview url在上下文中是相对的,但它是一个有效的url,因此spring将它构建为一个url,/my/page.html不是一个有效的网址。说问题是你应该以全双工方式配置 url 重写并在反向代理中解决问题,事实上,对于代码透视,如果它在 80 端口上提供服务的服务器上运行,代码将映射你的 url在 80 上,否则您应该手写您的网址,如下所示:
@RequestMapping(method = RequestMethod.GET, value = "/")
public RedirectView localRedirect()
{
final RedirectView redirectView = new RedirectView();
redirectView.setUrl("http://localhost:8080/my/index.html");
redirectView.setHosts();
return redirectView;
}
我尝试在我的电脑上运行两个应用程序实例,一个在 80 上运行,另一个在 8080 上运行,并且重定向工作正常
更新:
当您使用 RedirectView 时,重定向会通过 HttpServletResponse.sendRedirect 调用在服务器端发生,当然,如果您的服务器位于反向代理后面,您的服务器端应用程序将无法知道这一点。 当您在 Controller 中使用set Location Header 时,它是一个纯字符串,不会从您的servlet 环境传递。重定向发生在您的浏览器中,在这种情况下,您会收到一个相对 URL,因为您在 Controller 中设置了一个纯字符串,并且您的浏览器已经知道已被代理的服务器。
更新2 RedirectView 类的核心逻辑是一个名为 sendRedirect(...) 的 protected 方法。
/**
* Send a redirect back to the HTTP client
* @param request current HTTP request (allows for reacting to request method)
* @param response current HTTP response (for sending response headers)
* @param targetUrl the target URL to redirect to
* @param http10Compatible whether to stay compatible with HTTP 1.0 clients
* @throws IOException if thrown by response methods
*/
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
String targetUrl, boolean http10Compatible) throws IOException {
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
if (http10Compatible) {
HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);
if (this.statusCode != null) {
response.setStatus(this.statusCode.value());
response.setHeader("Location", encodedURL);
}
else if (attributeStatusCode != null) {
response.setStatus(attributeStatusCode.value());
response.setHeader("Location", encodedURL);
}
else {
// Send status code 302 by default.
response.sendRedirect(encodedURL);
}
}
else {
HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
response.setStatus(statusCode.value());
response.setHeader("Location", encodedURL);
}
}
该方法首先检索 url,然后如果 http10Compatible 为 true,最终将使用 response.sendRedirect(encodedURL);否则只需将您的相对 url 放入位置 header 中,而不传递 servlet api。在您的代码中,您没有提供用于阻止 Servlet api 的 sendRedirect 的事件 if 条件的数据。这可以解释为什么你的代码会出现问题。当然,在代码的任何其他分支中: http10Compatible at false 等等,您的代码只需在 Location header 中放入一个字符串即可,它会起作用,因为在您的浏览器中,将执行重定向的将到达相对网址。
对于这是否是Servlet API的bug的问题我可以放官方接口(interface)代码:
HttpServletResponse.java:
/**
* Sends a temporary redirect response to the client using the specified
* redirect location URL. This method can accept relative URLs; the servlet
* container must convert the relative URL to an absolute URL before sending
* the response to the client. If the location is relative without a leading
* '/' the container interprets it as relative to the current request URI.
* If the location is relative with a leading '/' the container interprets
* it as relative to the servlet container root.
* <p>
* If the response has already been committed, this method throws an
* IllegalStateException. After using this method, the response should be
* considered to be committed and should not be written to.
*
* @param location
* the redirect location URL
* @exception IOException
* If an input or output exception occurs
* @exception IllegalStateException
* If the response was committed or if a partial URL is given
* and cannot be converted into a valid URL
*/
public void sendRedirect(String location) throws IOException;
您可以阅读评论片段:
This method can accept relative URLs; the servlet
* container must convert the relative URL to an absolute URL before sending
* the response to the client
读完它,我可以说这不是一个错误,而是 servlet api 的一个功能,但是它放置了唯一知道其本身的服务器主机,因此它无法在反向代理的情况下工作。
希望这能很好地解释问题。
关于spring - RedirectView 不使用位置的相对路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51109259/