如何将带有斜杠的 URL 重定向到没有斜杠的相应 URL?

Posted

技术标签:

【中文标题】如何将带有斜杠的 URL 重定向到没有斜杠的相应 URL?【英文标题】:How to redirect URLs with trailing slash to the corresponding ones without it? 【发布时间】:2012-02-01 10:35:07 【问题描述】:

Spring MVC (3.0) 将带有和不带有斜杠的 URL 视为相同的 URL。

例如:

http://www.example.org/data/something = http://www.example.org/data/something/

我需要用尾部斜杠重定向 URL

http://www.example.org/data/something/

到没有它的 URL:

http://www.example.org/data/something

我需要在应用程序内部执行此操作(因此不要通过 Apache 等重写规则)。

一种方法是:

@ResponseStatus(value=HttpStatus.MOVED_PERMANENTLY)
@RequestMapping(value = "/data/something/")
public String dataSomethingRedirect(...) 
    return "redirect:/data/something";

但这通常有两个问题:

    控制器过多 参数问题:比如错误编码

问题

有没有办法拦截所有的 URL,如果它们有斜杠,将它们重定向到不带斜杠的相对 URL?

【问题讨论】:

如果一个总是重定向到另一个,它们不是同样等价的吗?实现这一目标所涉及的努力可能很高。如果您的担心完全是表面上的,那根本不值得。 在相对 url 链接方面它们并不等价 【参考方案1】:

你可以在你的 web 配置中列出你需要的所有重写规则

如果数量不多,您可以像这样配置重定向视图

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter 
  @Override
  public void addViewControllers(ViewControllerRegistry registry) 
    registry.addRedirectViewController("/my/path/", "/my/path")
      .setKeepQueryParams(true)
      .setStatusCode(HttpStatus.PERMANENT_REDIRECT); 

或者你可以创建一个自定义HandlerInterceptor

但是拦截器发生在请求被映射到特定的Controller.action 之前,您无法知道该上下文中的控制器和操作。

您所拥有的只是 HTTPServlet API 和请求+响应;所以你可以:

response.sendRedirect("http://example.org/whitout-trailing-slash");

你不想看的答案

在考虑 HTTP 时,这种行为(带有斜杠的 URL = 没有它的 URL)是完全“有效的”。至少这是 Spring 的默认行为,您可以使用 useTrailingSlashMatch (see javadoc) 禁用它。

因此在前端服务器上使用重写/重定向规则可能是一个解决方案;但同样,我不知道您的限制(也许您可以详细说明一下,我们可以找出其他解决方案?)。

【讨论】:

感谢您的回答。我正在考虑将 HandlerInterceptor 与我的“preHandle”版本一起使用(实际上我做到了)。问题是由于“搜索引擎优化”的原因,因为谷歌对带有和不带“/”的相同页面的排名不被认为是好的。 所以您正在考虑出于 SEO 原因放弃这种方法?这是个好主意 - 我认为 SEO 只是众多原因之一...... 好吧,我不知道这是一个很好的理由。但是您的访问者可能只会写您的家庭网址。其余请求通常来自您自己的链接。 仅供参考,有一个 JIRA 请求为这种情况添加一个 HandlerInterceptor (jira.springsource.org/browse/SPR-8473)。 "这种行为(带有斜杠的 URL = 没有它的 URL)在考虑 HTTP 时是完全“有效的”。”。不,就 HTTP 而言,http://example.org/blablahttp://example.org/blabla/(甚至http://example.org/blabla//)是不同的 URL。许多服务器会将它们视为相同,但这是调度机制的实现选择。 Apache Httpd 默认启用 (DirectorySlash On),但这是一个选项。【参考方案2】:

我认为您最好的选择是在进入 Spring web 的 servlet 之前使用 UrlRewriteFilter 执行此操作。这将确保您的重定向规则不会影响您的控制器。

请注意,您在 .war 项目中编写规则,而不是在带有 mod_rewrite 的 apache 中。

Go here 用于库在 googlecode 上的项目。

在urlrewrite.xml中写:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.1//EN" "http://www.tuckey.org/res/dtds/urlrewrite3.1.dtd">
<urlrewrite>
    <rule match-type="regex">  
      <note>Remove trailing slash</note>
      <from>^(.*)/$</from>
      <to type="redirect">$1</to>
    </rule>  
</urlrewrite>

在应用程序的 web.xml 中,添加:

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
       <init-param>
            <param-name>confPath</param-name>
            <param-value>/WEB-INF/urlrewrite.xml</param-value>
        </init-param>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

注意,web.xml 中过滤器的声明顺序很重要,因此请尝试在 spring 之前声明此顺序。

当然,这只是 UrlRewriteFilter 可以做的一小部分。

问候。

【讨论】:

好提示!实际上,您可能希望将 from 指定为 ^(/.*)/$ 。此正则表达式将在尾部斜杠的情况下跳过域名重定向,因为域名被视为“特殊情况”(www.domain.com 和 www.domain.com/ 都应返回 200)并且您不能/不要必须重定向它们 谷歌建议也应该首选 301 .. $1【参考方案3】:

这通常对我在 Spring 中使用 URLRewriteFilter 有效。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" 
 "http://www.tuckey.org/res/dtds/urlrewrite3.2.dtd">
<urlrewrite>
    <rule>
         <note>Remove trailing slash for SEO purposes</note>
         <from>/**/</from>
         <to type="permanent-redirect">%context-path/$1</to>
    </rule>
</urlrewrite>

【讨论】:

【参考方案4】:

不确定 spring 3.0 是否有此功能,但 spring 3.1 RequestMappingHandlerMapping 允许您设置“useTrailingSlashMatch”属性。默认为真。

我认为将其切换为 false 可以解决您的问题,但它会影响 RequestMappingHandlerMapping 在您的应用程序中处理的所有映射...因此您可能需要进行大量回归。

【讨论】:

【参考方案5】:

我同意@Brian Clozel 的观点:我不认为做你想做的事是个好主意。那么,为什么需要它?

无论如何,我认为最简单的解决方案是编写自定义javax.servlet.Filter。所以,没有Spring依赖。如果请求 URL 以斜杠结尾,您只需重定向到没有它的相同 url。但要小心:

必须将所有参数(GET 和 POST)添加为 GET 参数。您确定您的应用程序方法不可知

您可能会遇到一些编码问题。在过滤器中,您可以将 POST 参数编码为所需的编码。但是您的应用程序中未配置 GET 参数的默认编码。在 server.xml 中配置(如果是 Tomcat),默认值为 ISO-8859-1。

祝你好运!

【讨论】:

【参考方案6】:

基于SEO,我觉得做个区分很重要。

如果以斜杠结尾的 URL 存在,在搜索引擎中被索引并且 Internet 上有链接,则需要永久重定向 (301),如 Uddhav Kambli 所说。标准重定向 (302) 会比重复 URL 更好,但还不够好。

但是,如果 URL 从未存在过,它没有在 Internet 上编入索引并且没有外部链接,则该 URL 不存在。因此,404 页面未找到更适合。

WEB-INF/urlrewrite.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.1//EN"  "http://www.tuckey.org/res/dtds/urlrewrite3.1.dtd">
<urlrewrite>
    <rule match-type="regex">
        <note>Remove trailing slash</note>
        <from>^(.+)/$</from>
        <set type="status">404</set>
        <to>null</to>
    </rule>
</urlrewrite>

为了完成配置...

添加到 WEB-INF/web.xml

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    <init-param>
        <param-name>confPath</param-name>
        <param-value>/WEB-INF/urlrewrite.xml</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

Maven

<dependency>
    <groupId>org.tuckey</groupId>
    <artifactId>urlrewritefilter</artifactId>
    <version>4.0.3</version>
</dependency>

【讨论】:

【参考方案7】:

我发现这也可以通过在 @Configuration 中的某处使用 ErrorViewResolver bean 来更简单地处理:

@Autowired
private DefaultErrorViewResolver defaultErrorViewResolver;

@Bean
ErrorViewResolver errorViewResolver() 
    return new ErrorViewResolver() 
        @Override
        public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) 
            if(model.containsKey("path") && !model.get("path").toString().endsWith("/")) 
                return new ModelAndView("redirect:"+model.get("path") + "/");
            
            return defaultErrorViewResolver.resolveErrorView(request, status, model);
        
    ;

我不确定这是好的还是坏的做法,但它在我的情况下是有效的,并且完全符合我对任意路径“foo”的要求:响应 302 重定向到 /foo/当您请求 /foo 并在您请求 /foo 时做出应有的响应。

【讨论】:

以上是关于如何将带有斜杠的 URL 重定向到没有斜杠的相应 URL?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET 重定向 URL

如何禁用 301 重定向,在 Apache 中将斜杠添加到目录名称

Web.config:使用尾部斜杠将URL输入重定向到URL而不使用尾部斜杠

Django - 从斜杠重定向到非斜杠

当 url 没有斜杠时,AWS s3 强制 302 重定向 - 需要 301s

将 HTTP 重定向到 HTTPS 的问题