16

How to dynamically create urls/links like: www.restaurant.com/restaurant/restaurant-name-without-some-characters-like-space-coma-etc/132

what are the keywords i can use to google some articles on this topic? (how to genererate and handle this kind of urls inside asp.net mvc)

There are some questions: How to generate links? (store slugs in db?) Redirect or not if slug isn't canonical?

edit: apparently they are called slugs

Ante
  • 8,567
  • 17
  • 58
  • 70

4 Answers4

6

You have to use something as follows.

Routes.MapRoute(
    "Post",
    "posts/{id}/{*title}",
    new { controller = "Posts", action = "view" }
);

And a simple extension method:

public static class UrlExtensions
{

    public static string ResolveSubjectForUrl(this HtmlHelper source, string subject)
    {
        return Regex.Replace(Regex.Replace(subject, "[^\\w]", "-"), "[-]{2,}", "-");
    }

}
m3kh
  • 7,881
  • 2
  • 31
  • 37
  • what's "*" for in "{*title}"? – Ante Feb 01 '10 at 05:56
  • That is a catch-all parameter. Everything after id is title. Also that makes the last parameter optional as well. – m3kh Feb 01 '10 at 06:06
  • 1
    When and where do you call the UrlExtensions.ResolveSubjectForUrl method? – Chris May 21 '11 at 04:00
  • @Chris, you can store the generated slug in the database or use this method inline in the markup when you're generating links list. – m3kh May 21 '11 at 09:43
4

I've always stored slugs in the database alongside whatever entity would be referenced by them. So for a blog post, you would have a "slug" field in the "posts" table.

To handle it in ASP.Net MVC is easy - you just use a regular route that would capture the slug in a parameter (possibly even just using {id}), and then your controller would look up the slug in the database, load the entity, and display it as normal.

Although you can use a simple RegEx to replace spaces and whatnot to generate your slugs, in reality this breaks down pretty quickly. You need to consider all kinds of characters that may appear in your titles. Michael Kaplan's blog has been linked to heavily for this exact purpose; he's shared a function that will strip diacritical marks from strings.

So, your "generate slug" algorithm should generally take the form of:

  1. Trim the string of leading/trailing whitespace
  2. Strip diacritical marks using Michael Kaplan's function or equivalent
  3. Lowercase the string for canonicalization
  4. Replace all the non-word characters with dashes
Community
  • 1
  • 1
womp
  • 115,835
  • 26
  • 236
  • 269
  • Why would you store them in the database? – L-Four Aug 20 '13 at 14:06
  • why? you do not have to do so. Generally speaking I think it is more optimal. For example what if you want to edit that particular slug/url. I dont know perhaps make it shorter or whatever. you will not be able to do it without changing the title field. Does it make sense? – Victor_Tlepshev Apr 23 '17 at 01:41
2

One way to do this is the following on your string

 string cleanString = originalString.ToLower().Replace(" ", "-"); // ToLower() on the string thenreplaces spaces with hyphens
 cleanString = Regex.Replace(cleanString, @"[^a-zA-Z0-9\/_|+ -]", ""); // removes all non-alphanumerics/underscore/hyphens

Now you can pass the cleanString (for titles,names etc) into the ActoinLink/Url.Action parameters and it will work great.

The pattern was taken from http://snipplr.com/view/18414/string-to-clean-url-generator/

I'm not 100% on the Regex pattern, if some Regex hero can chime in and offer a better one that would be great. From testing the Regex, it doesn't match spaces, but this shouldn't be a problem because the first line replaces all spaces with hyphens.

Update:

To use this code, you need to setup your routes to accept extra parameters.

We'll use a blog article title as an example.

        routes.MapRoute(
            "",                                              // Route name
            "View/{ID}/{Title}",                           // URL with parameters
            new { controller = "Articles", action = "View"}  // Parameter defaults
        );

In your ASP.NET MVC views, you can then do the following:

  <%= Html.ActionLink("View Article", "View", "Articles", new { ID = article.ID, Title = Html.SanitizeTitle(article.Title) }, null) %>

In the previous example, I use SanitizeTitle as an HTML helper.

public static string SanitizeTitle(this HtmlHelper html, string originalString)
{
     string cleanString = originalString.ToLower().Replace(" ", "-"); // ToLower() on the string then replaces spaces with hyphens
     cleanString = Regex.Replace(cleanString, @"[^a-zA-Z0-9\/_|+ -]", ""); // removes all non-alphanumerics/underscore/hyphens
     return cleanString;
}
David Paquette
  • 524
  • 6
  • 14
Omar
  • 39,496
  • 45
  • 145
  • 213
  • I'm no regex hero but I think this would be better: input = Regex.Replace(input, @"[ |\\/]", "-"); // Replace "breaking" chars with "-" input = Regex.Replace(input, @"[^a-zA-Z0-9-_.~]", ""); // Replace remaining non-safe (RFC3986) chars with "" input = input.Trim(new [] {'-', '_', '.', '~'}).ToLower(); // Trim leading or trailing non-alphanumeric chars and convert to lowercase – JohannesH Feb 01 '10 at 06:27
  • 1
    Crap... No line-breaks in comments. – JohannesH Feb 01 '10 at 06:28
2

I just asked a relevant question today on SO regarding to generating slug, aka/ slugify a title.

When fetch a URL with slug, you can either create an action that takes both ID (and other required arguments) and slug in and simply ignore the slug.

public ActionResult Foobar(int id, string slug)
{
    //-- Do Something
}

Or more elegant, Use map route to ignore trialing string behind your URL and map to Foobar(int id).

Community
  • 1
  • 1
Trav L
  • 14,732
  • 6
  • 30
  • 39