使用路由操作 url

Manipulate the url using routing

在我的网站中,我定义了以下路线:

routes.MapRoute(
   name: "Specific Product",
   url: "product/{id}",
   defaults: new { controller = "", action = "Index", id = UrlParameter.Optional }
);

通过这种方式,我希望客户能够添加产品 ID 并转到产品页面。

SEO 顾问说,如果我们可以在 URL 上添加产品描述,例如产品名称或其他内容,那就更好了。所以 URL 应该看起来像:

/product/my-cool-product-name/123

/product/my-cool-product-name-123

当然,描述存储在数据库中,我无法通过 url 重写(或者我可以吗?)

我是否应该在我的控制器上添加重定向(这似乎可以完成工作,但感觉不对)

在我检查过的几个网站上,它们确实以 301 Moved Permanently 响应。这真的是最好的方法吗?

更新

根据 Stephen Muecke 的评论,我检查了 SO 上发生的事情。

建议的 url 是我自己的 ,我打开控制台查看任何重定向。这是屏幕截图:

所以,首先要特别感谢 @StephenMuecke 提供了 slug 的提示以及他建议的 url。

我想 post 我的方法是 url 和其他几篇文章的混合。

我的目标是让用户输入 url 比如:

/product/123

并且当页面加载时在地址栏中显示如下内容:

/product/my-awsome-product-name-123

我检查了几个有这种行为的网站,似乎我检查的所有网站都使用了 301 Moved Permanently 响应。即使是我的问题中显示的 SO,也使用 301 添加问题的标题。我认为会有一种不同的方法不需要第二次往返....

所以我在这种情况下使用的总解决方案是:

  1. 我创建了一个 SlugRouteHandler class 看起来像:

    public class SlugRouteHandler : MvcRouteHandler
    {
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            var url = requestContext.HttpContext.Request.Path.TrimStart('/');
    
            if (!string.IsNullOrEmpty(url))
            {
                var slug = (string)requestContext.RouteData.Values["slug"];
                int id;
    
                //i care to transform only the urls that have a plain product id. If anything else is in the url i do not mind, it looks ok....
                if (Int32.TryParse(slug, out id))
                {
                    //get the product from the db to get the description
                    var product = dc.Products.Where(x => x.ID == id).FirstOrDefault();
                    //if the product exists then proceed with the transformation. 
                    //if it does not exist then we could addd proper handling for 404 response here.
                    if (product != null)
                    {
                        //get the description of the product
                        //SEOFriendly is an extension i have to remove special characters, replace spaces with dashes, turn capital case to lower and a whole bunch of transformations the SEO audit has requested
                        var description = String.Concat(product.name, "-", id).SEOFriendly(); 
                        //transform the url
                        var newUrl = String.Concat("/product/",description);
                        return new RedirectHandler(newUrl);
                    }
                }
    
            }
    
            return base.GetHttpHandler(requestContext);
        }
    
    }
    
  2. 从上面我还需要创建一个 RedirectHandler class 来处理重定向。这实际上是 here

    的直接复制
    public class RedirectHandler : IHttpHandler
    {
        private string newUrl;
    
        public RedirectHandler(string newUrl)
        {
            this.newUrl = newUrl;
        }
    
        public bool IsReusable
        {
            get { return true; }
        }
    
        public void ProcessRequest(HttpContext httpContext)
        {
            httpContext.Response.Status = "301 Moved Permanently";
            httpContext.Response.StatusCode = 301;
            httpContext.Response.AppendHeader("Location", newUrl);
            return;
        }
    }
    

有了这 2 个 classes,我可以将产品 ID 转换为 SEO 友好 urls。

为了使用这些,我需要修改我的路线以使用 SlugRouteHandler class,这导致:

  1. 从路线

    呼叫SlugRouteHandlerclass
    routes.MapRoute(
       name: "Specific Product",
       url: "product/{slug}",
       defaults: new { controller = "Product", action = "Index" }
    ).RouteHandler = new SlugRouteHandler();
    

他评论中提到的link@StephenMuecke的用法来了。

我们需要找到一种方法将新的 SEO 友好 url 映射到我们的实际控制者。我的控制器接受一个整数 ID,但 url 将提供一个字符串。

  1. 我们需要创建一个 Action 过滤器来处理调用控制器之前传递的新参数

    public class SlugToIdAttribute : ActionFilterAttribute
    {
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var slug = filterContext.RouteData.Values["slug"] as string;
            if (slug != null)
            {
                //my transformed url will always end in '-1234' so i split the param on '-' and get the last portion of it. That is my id. 
                //if an id is not supplied, meaning the param is not ending in a number i will just continue and let something else handle the error
                int id;
                Int32.TryParse(slug.Split('-').Last(), out id);
                if (id != 0)
                {
                    //the controller expects an id and here we will provide it
                    filterContext.ActionParameters["id"] = id;
                }
            }
            base.OnActionExecuting(filterContext);
        }
    }
    

现在控制器将能够接受以数字结尾的非数字 ID 并提供其视图,而无需修改控制器的内容。我们只需要在控制器上添加过滤器属性,如下一步所示。

我真的不在乎产品名称是否真的是产品名称。您可以尝试获取以下 urls:

\product3

\product\product-name-123

\product\another-product-123

\product\john-doe-123

你仍然会得到 ID 为 123 的产品,尽管 url 不同。

  1. 下一步是让控制器知道它必须使用特殊的文件管理器

    [SlugToId]
    public ActionResult Index(int id)
    {
    }