RedirectView不使用相对路径作为位置

时间:2018-06-29 20:52:34

标签: spring spring-mvc reverse-proxy

我有一个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标头设置为相对路径:/my/page.html。但是,实际上,它设置为完整路径:http://localhost/my/page.html

这引起了一些问题,因为此应用程序运行在Docker容器中,该容器认为它正在为端口80提供服务;您可能已经注意到完整路径在URL中删除了:8080端口说明符。 Docker容器在反向代理后面运行,该反向代理将对8080的请求映射到该容器。如果location标头是相对的并设置为/my/page.html,则预期浏览器客户端将使用正确的主机名(localhost:8080),因此反向代理会将其重定向到正确的主机名。页面。

从代码中可以看到,我尝试将ContextRelative对象中的RedirectView选项设置为true。这里还有我想念的东西吗?

编辑

@RequestMapping(method = RequestMethod.GET, value = "/")
public void localRedirect(HttpServletResponse response) {
    response.setStatus(HttpServletResponse.SC_FOUND);
    response.setHeader("Location", "/my/page.html");
}

我已经重定向到可以使用以上代码的位置。但是,我仍然很好奇是否有人知道用Spring的RedirectViewRedirectStrategy完成上述操作的方法,我很乐意接受该解决方案。

1 个答案:

答案 0 :(得分:1)

您的代码的行为是正确的,因为您是说您的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;
}

我尝试在PC上运行两个应用程序实例,一个在80上运行,另一个在8080上运行,重定向正常工作

更新

当您使用RedirectView时,重定向将通过HttpServletResponse.sendRedirect调用在服务器端发生,当然,如果您的服务器停留在反向代理之后,则服务器端应用程序将无法识别该重定向。 在控制器中使用set Location Header时,它是纯字符串,请勿从servlet环境中传递。重定向发生在您的浏览器中,在这种情况下,该重定向获取了一个相对URL,因为在控制器中您设置了纯字符串,并且浏览器已经知道已经代理的服务器。

更新2 RedirectView类的核心逻辑是一个受保护的方法,称为sendRedirect(...)。

/**
     * 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);否则,只需将您的相对网址放在位置标头中,而无需传递servlet api。在您的代码中,您不会提供有效的if条件来阻止Servlet api的sendRedirect的数据。这可以解释为什么在您的代码中有问题。当然,在任何其他代码分支中:http10Compatible为false,等等,您的代码只需在Location标头中放置一个字符串即可,因为在执行重定向的浏览器中会到达一个相对的URL。

对于这是否是Servlet API的错误的问题,我可以输入官方接口代码:

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

阅读它,我可以说这不是一个bug,而是servlet api的一个功能,但是,它放置了唯一知道自身的服务器主机,因此,如果使用反向代理,它将无法正常工作。

我希望这可以很好地解释问题。