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?
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
While I don't know any specifics of how StackOverflow manage it, here's an overview of how you could do it
This ensures the URL is always the correct one and avoids possible embarrassing fake URLs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With