Adding ID and title to URL slugs in ASP.NET MVC

2019-01-12 17:19发布

问题:

How do you redirect a request in ASP.NET MVC to its correct canonical version if part of the URL is missing?

Using Stack Overflow as an example, the site adds the question title to the end of its routes, but uses the question ID in the route to actually find the question. If the title gets omitted you will be redirected to the correct URL.

For example, visiting the URL:

stackoverflow.com/questions/9033 

will redirect to

stackoverflow.com/questions/9033/hidden-features-of-c

How does this work?

回答1:

First create a route:

routes.MapRoute( 
    "ViewProduct", 
    "Products/{id}/{productName}", 
    new { controller = "Product", action = "Details", id = "", productName = "" } 
);

Then create the Action method like so:

public ActionResult Details(int? id, string productName) 
{ 
    Product product = ProductRepository.Fetch(id); 

    string realTitle = UrlEncoder.ToFriendlyUrl(product.Title); 
    string urlTitle = (productName ?? "").Trim().ToLower(); 

    if (realTitle != urlTitle)
    { 
        string url = "/Products/" + product.Id + "/" + realTitle; 
        return new PermanentRedirectResult(url);
    } 

    return View(product); 
}

You're basically comparing the entity title in the URL with the one stored in the database, if they don't match then perform a 301 permanent redirect. Make sure it's a 'permanent' redirect (301 status code) instead of a temp redirect (302). This way search engines will treat it as a permanent change of the URL and will update their indexes accordingly, this might happen if the title of your entity changes after a search engine has indexed it (e.g. someone changes the name of the product).

Another thing to be aware of, if your title allows any free text, you need to strip out any characters that are invalid for a URL, and make it more readable for humans and search engines alike, hence the UrlEncoder.ToFriendlyUrl method in the code above, the implementation is below:

public static class UrlEncoder 
{ 
    public static string ToFriendlyUrl (this UrlHelper helper, 
        string urlToEncode) 
    { 
        urlToEncode = (urlToEncode ?? "").Trim().ToLower(); 

        StringBuilder url = new StringBuilder(); 

        foreach (char ch in urlToEncode) 
        { 
            switch (ch) 
            { 
                case ' ': 
                    url.Append('-'); 
                    break; 
                case '&': 
                    url.Append("and"); 
                    break; 
                case '\'': 
                    break; 
                default: 
                    if ((ch >= '0' && ch <= '9') || 
                        (ch >= 'a' && ch <= 'z')) 
                    { 
                        url.Append(ch); 
                    } 
                    else 
                    { 
                        url.Append('-'); 
                    } 
                    break; 
            } 
        } 

        return url.ToString(); 
    } 
}

So when you write out the URLs into the View, be sure to encode the titles with this method e.g.

<a href="/Products/@Model.Id/@Url.ToFriendlyUrl(Model.Title)">@Model.Title</a>

I've written a blog post about this here http://www.dominicpettifer.co.uk/Blog/34/asp-net-mvc-and-clean-seo-friendly-urls



回答2:

While I don't know any specifics of how StackOverflow manage it, here's an overview of how you could do it

  • Retrieve the question from the database using the ID
  • Convert the stored question title into a URL compatible slug
  • If the converted title slug does not match the slug passed in the URL then redirect using the converted title slug.

This ensures the URL is always the correct one and avoids possible embarrassing fake URLs



回答3:

You should learn about ASP.net MVC routing mechanisms, as Stackoverflow uses this technology. It is quite a complex question to be answered here, but you will find lots of learning resources out there, like: http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx



回答4:

Here is an example of how they might do it, but I believe your asking how it can be done and this should work.

First stage is to set-up 2 routes in the Global.asax

routes.MapRoute("WithQuestion", "questions/{id}/{name}", new { controller = "Questions", action = "Question", id = "1" });
routes.MapRoute("WithoutQuestion", "questions/{id}", new { controller = "Questions", action = "WithoutQuestion", id="1"});

Now in our Questions controller,

    public ActionResult Question(int id)
    {
        //Load the question as we have the name appended.
        // We could actually do a little validation here as well
        return View();
    }

    public ActionResult WithoutQuestion(int id)
    {
        //Load the question object
        //Generate the full URL and redirect
        return Redirect(FullURL)
    }

This is a very basic example but shows how you could do it.



回答5:

@sunday i have tried it , but still i was facing issue . I need to give the url as

Products?id=4&productName=new-blog

Then i got solution .This helped me . We need make sure CUSTOM route is ABOVE the default route

Now it works fine.