Skip to main content
 首页 » 编程设计

spring中RedirectView 不使用位置的相对路径

2025年05月04日75qq78292959

我有一个 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 的 RedirectViewRedirectStrategy 来完成上述任务,我很乐意接受该解决方案。

请您参考如下方法:

你的代码的行为是正确的,因为你说你的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 的一个功能,但是它放置了唯一知道其本身的服务器主机,因此它无法在反向代理的情况下工作。

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