代码之家  ›  专栏  ›  技术社区  ›  scottysseus

重定向视图不使用相对路径作为位置

  •  1
  • scottysseus  · 技术社区  · 8 年前

    我有一个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服务;您可能已经注意到完整路径删除了 :8080 URL中的端口说明符。Docker容器在反向代理后面运行,该代理将请求映射到 8080 去集装箱。如果 位置 标题是相对的,设置为 /我的/page.html ,预期浏览器客户端将使用正确的主机名( localhost:8080 ,因此反向代理会将其重定向到正确的页面。

    从我的代码可以看出,我试着设置 ContextRelative 中的选项 RedirectView 反对为真。我还缺什么吗?

    编辑

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

    我已经用上面的代码得到了工作的重定向。但是,我仍然很好奇是否有人知道如何用 重定向视图 RedirectStrategy 从春天开始,我很乐意接受这个解决方案。

    1 回复  |  直到 8 年前
        1
  •  2
  •   Valerio Vaudi    8 年前

    代码的行为是正确的,因为您说您的重定向视图url在上下文上是相对的,但是它是一个有效的url,因此spring将它构建为一个url,/my/page.html不是一个有效的url。说的问题是,你应该配置全双工方式重写的url,并在反向代理中解决这个问题,事实上,对于代码来说,如果它运行在一个服务于80端口的服务器上,那么代码会将你的url映射到80上,否则你应该像下面这样手工编写url:

    @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.sendrirect调用一起发生,当然,如果您的服务器位于反向代理之后,您的服务器端应用程序将无法知道它。 当您在控制器中使用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);否则只需将相对url放在location头中,而不传递ServletAPI。在您的代码中,您没有为active the if条件提供数据,该条件阻止了servlet api的sendRedirect。这可以解释为什么在代码中有问题。当然,在任何其他代码分支中:http10compatible at false等等,您的代码只需在location header中放置一个字符串,它就可以工作,因为在您的浏览器中,执行重定向的会到达一个相对的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,而是ServletAPI的一个特性,但是它把唯一知道自身的服务器主机放在了这里,因此它不能与反向代理一起工作。

    我希望这能很好地解释这个问题。