Wednesday, August 24, 2011

It's Alive! Cleaning up broken URLs with MVC Routing

If your website has been around for a while, then you've probably got some dead links out there on the web. You may have changed your site's folder structures a few times, changed technologies from classic asp, to php, to ASP.NET web forms, to MVC. I've had to tackle this problem continuously, since my site Connemara.net, maintained more or less as a hobby at this stage, has been around since 1996.

One of the benefits of using ASP.NET MVC is routing. Although routing is not confined to MVC, located as it is in System.Web.Routing, it is strongly associated with MVC. With ASP.NET Web Forms, URLs generally correspond to files on disk, so for example the address www.connemara.net/words/index.aspx?id=079 meant there was a file called "index.aspx" in the top-level folder called "Words" and it is going to look for something with an id of 079, most likely a record in a table in a database. It's the Pompidou Centre URL pattern, one where the skeleton is on the outside, for everyone to see.

Flickr photo
FlickrPompidou Centre, by Edward Langley. Postmodernist icon, shit URL structure

Routing, on the other hand, places resources front and centre. A resource is anything important enough to have its own address. Having URLs that reflect your resources is part of what's called the Resource-Oriented Architecture in 'RESTful Web Services', which you should read.

So, for example on Connemara.net the first 'Letter from Home' that my friend Eugene wrote for the 'words' section is a resource: it's something you'd make a link to, but one whose original address was http://www.connemara.net/words/letter/no.1.htm. That 'no.1.htm' file has long since stopped being served from that address. Which is a real pity, because ol' Euge wrote some nice stuff back then, and it'd be nice to preserve it.

Google Webmaster Tools Crawl Errors page is where links go to die, so you should buy some flowers and pay your respects from time to time. Your users, if you're lucky enough to have any, tend also to tell you all about your broken links. Connemara.net goes back to Oct. '96, so there's plenty of early defunct ".htm"s, ".html"s, and even ".tmpls" littering the far corners of the web. Then there are what I call "The PHP Years". My first ever web scripting language. Good times. But now the party's over, and it's time to clean up the condoms. That's where MVC routing comes in. How to fix up a link like www.connemara.net/words/article.php?id=075?

I can start by making a route template that contains the literal value "words", and catches anything after that (and the forward slash). So "/words/article.php?id=075" matches, and the 'oldPath' parameter gets the value 'article.php?id=075'. Just map a route like this:
routes.MapRoute("OldWordsArticle",
                "Words/{oldPath}",
                new { controller = "Words", action = "RedirectToArticle" } );

routes.MapRoute("WordsArticle",
                "Words/Articles/{id}/{hyphenatedTitle slug}",
                new { controller = "Words", action = "Article", hyphenatedTitle slug = UrlParameter.Optional } );
That route is what's called greedy. It'll catch any request to the the Words folder as long as it's positioned before the more refined route patterns. Within WordsController, I strip out the id from oldPath ('/article.php?id=123'), look up what that article's new id is, and then reroute the request to a new route, 'WordsArticles'.
public ActionResult RedirectToArticle(string oldPath)
{
    var oldId = ResolveOldId();

    // get the Id of the article to generate the correct URL
    var article = ArticleRepository.Search<Article>(oldId).FirstOrDefault();

    if(article != null)
        return new RedirectToRouteResult("WordsArticle",
                                         new RouteValueDictionary {
                                             { "controller", "Words" },
                                             { "action", "Article" },
                                             { "id", article.Id }
                                             { "slug", article.Slug}
                                         });

    // no article found?
    ViewBag.Message = "No article with an id of " + oldId + " found";
    return View("NotFound");
}
The main point here is that I'm using one route pattern to catch bad old links, and steering them to the correct route. Normally, having matched an incoming request to a route, you then hit a controller method which returns a view, which is a type of ActionResult. But in this case, I return a different type of result, a RedirectToRouteResult, which turns the original route into a recursive one. If you're not careful you could end up in an infinite route black hole and crash the internet.

So now if I browse to www.connemara.net/words/article.php?id=075, the "OldWordsArticle" route catches the request, RedirectToArticle() deals with it and routes it to "WordsArticle" which knows how to serve up a normal ActionResult/View, with a brand, spanking new, RESTy URL of http://www.connemara.net/words/articles/22/michael-gibbons--person-in-profile. "It's Alive! It's Alive!"

EDIT 23/5/2012

Since I wrote this post, Connemara.net has been revamped, courtesy of Noel at Connemara Publications. I removed the links to the old Connemara.net pages that are mentioned, but the thrust of the post is unaffected.

50 comments:

  1. 1. I suggest you make your urls lowercase such as words/articles/{id}/{hyphenatedTitle}.

    2. Your hyphenated title is commonly referred to as a slug see http://en.wikipedia.org/wiki/Slug_(web_publishing)

    Why don't you add this in the route value dictionary when you redirect? Otherwise the user / search engine only sees as far as the id in the url.

    3. Your repository call looks bizarre and it looks like you are not using a service locator. For starters I would be making these calls via an interface both for article search and ResolveOldId;

    interface IArticleRepository
    {
    Article Search(int id);
    }

    interface IIdResolution
    {
    int ResolveOldId(string currentUrlPart);
    }

    so your calls would be:

    var oldId = _idResolution.ResolveOldId(oldPath);
    and later
    var article = _articleRepository.Search(oldId);

    ReplyDelete
  2. Thanks boon,

    1. Why? I mean, consistency would be nice but why all lowercase?

    2. Never knew it was called a slug! Thanks. It isn't in the redirected route because I got lazy. Well spotted. You're right, of course, it should be in there. In fact, that's kinda one of the main points. Curse you. But thanks.

    3. No, not using a service locator. Reckon it would be overkill for my site.

    ReplyDelete
  3. My 2 cents:

    1. I think lowercase is just more of a convention - do browsers/web servers treat them as case sensitive? I know IIS doesn't seem to care, which is what your ASP.NET MVC site is running on.

    2. Slug is such a cool word for that. I can already see your urls oozing all over and leaving snail trails all over my Chrome man!

    ReplyDelete
  4. As for lower case its kind of a convention. Search engines may view differently cased urls which point to the same resource as different resources. Some hosts care about the case when accessing a resource on disk (not relevant in MVC / windows hosts).

    ReplyDelete
  5. Thank you for sharing this guide, I just followed this and it worked perfect.

    ReplyDelete
  6. Hi, Ralph. . .

    I googled "Letters from home" in an idle moment recently, and one of the hits brought me here; it was your comment about how LFH was lost. Well, as it happens, I have a photocopy of the LFM,and I can post it to you if you like. Email me at: euge1936@gmail.com

    cheers!

    euge

    ReplyDelete
  7. Here at this site really the fastidious material collection so that everybody can enjoy a lot.
    container rental near me

    ReplyDelete
  8. You need a residential cleaning organization that is adaptable. An organization that can address your quick needs is perfect.part time helper

    ReplyDelete
  9. In spite of the fact that this procedure has demonstrated great cleaning results, this cleaning strategy has not had the option to completely clean overwhelming ruining carpet due to the innovation's restriction.Carpet and Rug Cleaning Fayetteville NC 28303

    ReplyDelete
  10. The writer has written this blog in a very idiomatic manner.
    OC tree removal service

    ReplyDelete
  11. You may even choose to purchase a similar tangle, if your old most loved is as yet accessible. Nonetheless, on the off chance that you choose to settle on cover substitution, you will likely wind up spending a ton of cash.
    Brooklyn NY Carpet Cleaning

    ReplyDelete
  12. نحن نمتلك مجموعة من الخبراء والمتخصصين فى شركة تنظيف بابها ونحن نستطيع التعامل مع كافة المساحات المختلفة فلا يهم ان كنت تمتلك منزل او فيلا فأن لدينا خبرات كبيرة تمكنا من تقديم خدماتنا على اكمل وجه ولدينا عمال وفنيين محترفين ولهم خبرات مختلفة نقدم ايضآ تنظيف لواجهات الشركات والفنادق. فكل ما تحتاجه من معدات واجهزة ومنظفات ذات جودة عالمية موجودة بشركتنا فنحن نسعى فقط لارضاء العميل أولآ واخيرآ

    شركة عزل خزانات بخميس مشيط
    شركة مكافحة حشرات بخميس مشيط
    شركة غسيل مجالس بخميس مشيط
    شركة غسيل خزانات المياه بخميس مشيط
    شركة غسيل خزانات بخميس مشيط
    شركة تنظيف شقق بابها

    ReplyDelete
  13. On this subject internet page, you'll see my best information, be sure to look over this level of detail. Click here

    ReplyDelete
  14. The writer has written this blog in the most artistic way. Splendid!
    bachelor party strippers in Scottsdale

    ReplyDelete
  15. The Cleaning team leader would be liable in bringing and gathering all the equipments and tools used in cleaning the building before and after the cleaning proceedings.clothes hamper

    ReplyDelete
  16. A very awesome blog post. We are really grateful for your blog post. You will find a lot of approaches after visiting your post. Sofa and Shampoo Cleaning Abu Dhabi

    ReplyDelete
  17. Positive site, where did u come up with the information on this posting?I have read a few of the articles on your website now, and I really like your style. Thanks a million and please keep up the effective work.
    best junk removal service

    ReplyDelete
  18. I definitely enjoying every little bit of it. It is a great website and nice share. I want to thank you. Good job! You guys do a great blog, and have some great contents. Keep up the good work. Furnace cleaning Calgary

    ReplyDelete
  19. When your website or blog goes live for the first time, it is exciting. That is until you realize no one but you and your. steam mop reviews

    ReplyDelete
  20. Thanks a lot for sharing this excellent info! I am looking forward to seeing more posts by you as soon as possible! I have judged that you do not compromise on quality. steam mop

    ReplyDelete
  21. The feature is the use of cleaning compound or powder into the base piece of rug utilizing a mechanized counter turning brush machine to open up the rug fiber and permit the compound to settle inside, bringing about intensive profound floor covering cleaning result. Carpet Cleaning

    ReplyDelete
  22. This particular papers fabulous, and My spouse and i enjoy each of the perform that you have placed into this. I’m sure that you will be making a really useful place. I has been additionally pleased. Good perform! What is the tallest toilet you can buy

    ReplyDelete
  23. All that hassle can add stress, months to the process, and in the end after paying the agent’s expensive fees, you may or may not be ahead of the game.

    We Buy Houses Greenfield WI


    ReplyDelete
  24. Disposable nuisance dust masks to protect the wearer from exposure to coarse, non-toxic particles. Molded to the face with an elastic strap. These masks are lightweight so are easy to breathe through and do not restrict the vision.

    3m masks

    ReplyDelete
  25. Webactueel schakel je in als je een maatwerk WordPress website laten maken wilt. Door een maatwerk website te ontwikkelen is het mogelijk om meer leads te genereren. Daarbij hebben we altijd oren voor de wensen en eisen die jij als ondernemer hebt als het om jouw website gaat. Bovendien gaat het om meer dan alleen een website. Ook het toepassen van de juist online marketingstrategie helpt hier in grote mate bij. Wij kunnen dit allemaal voor je verzorgen.

    Zie: Webactueel

    ReplyDelete
  26. It is also important to check whether the company is accredited by an organization like BBB Local Wolf Appliance Repair Company In Culver City

    ReplyDelete
  27. Alternatively you could buy a disposable brush, though these are quite expensive. broom and dustpan combo

    ReplyDelete
  28. i never know the use of adobe shadow until i saw this post. thank you for this! this is very helpful. Water Heater Repair Sun City

    ReplyDelete
  29. Cleaning is important for the healthy environment. Get the best quality long-lasting oriental rugs online at most reasonable prices from Shahbanu Rugs.

    ReplyDelete
  30. 우리카지노 100%안전 검증된 카지노사이트 만 엄선하여 소개해드립니다 국내 업계1위 우리카지노계열 전통이 있는 온라인카지노 에서 안전하고 편안한 게임을 ..

    우리카지노

    ReplyDelete
  31. I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you post. broom

    ReplyDelete
  32. What a fantabulous post this has been. Never seen this kind of useful post. I am grateful to you and expect more number of posts like these. Thank you very much. aircon servicing promotion

    ReplyDelete
  33. It's no wonder there have been so many well-publicized public campaigns to reduce disease and illnesses tell consumers to use hand liquid sanitizer frequently. antibacterial hand sanitizer

    ReplyDelete
  34. Its a great pleasure reading your post.Its useful information. as we provide government facilities services at affordable prices. for more info visit our website.

    ReplyDelete
  35. Swiftly this amazing site may well definitely turn out to be well-known between almost all writing a blog men and women, for your careful content and even assessments. bank cleaning

    ReplyDelete
  36. We have sell some products of different custom boxes.it is very useful and very low price please visits this site thanks and please share this post with your friends. Corona drain cleaning

    ReplyDelete
  37. Great work man you have post a great post it will help people very much keep it do more for people like that https://anjun1.net/.

    ReplyDelete
  38. Steam mops come with their own self containing water bucket that is built in. steam mop

    ReplyDelete
  39. فنحن نقوم بكافة أعمال التنظيف للشقق والفلل والقصور نعمل على تنظيف كافة المنزل بداية من الأرضيات سواء كانت بلاط أو سراميك أو رخام و أيضا نقوم بتنظيف فرش الأرضيات سواء موكيت أو سجاد بأكثر من طريقة سواء باستخدام البخار أو بجهاز الرغوة الالية أو باستخدام الفرشاة الاليه. كما نقوم بتنظيف الأثاث بأجود المواد التي لا تؤثر على درجة الالوان أو حالة الأخشاب كما نقوم بتلميع الزجاج ممها بلغ ارتفاعة.

    شركة غسيل موكيت بابها
    شركة عزل خزانات بابها
    شركة تنظيف خزانات المياه بابها
    شركة تنظيف كنب بابها
    شركة رش مبيدات بابها
    شركة رش مبيدات بابها
    شركة مكافحة الحشرات بابها

    ReplyDelete
  40. Sono soddisfatto delle informazioni che mi fornite e grazie per questo perché a volte le persone affrontano questo problema. poiché forniamo servizi di pulizia a prezzi convenienti. per maggiori informazioni visita il nostro sito web Migliore impresa di pulizie a Novara

    ReplyDelete
  41. This particular is usually apparently essential and moreover outstanding truth along with for sure fair-minded and moreover admittedly useful My business is looking to find in advance designed for this specific useful stuffs… Brick cleaning London

    ReplyDelete
  42. It's no secret that the country's not in the best position financially right now. It's not only individuals who are feeling the crunch but businesses as well. If companies are to stay in business during this time of financial trouble, their owners must be creative and try to save on expenses wherever they can. construction cleanup services

    ReplyDelete
  43. I admire this article for the well-researched content and excellent wording. as we provide Carpet Cleaning Harpenden at affordable prices. for more info visit our website.

    ReplyDelete
  44. Very informative post! There is a lot of information here that can help any business get started with a successful social networking campaign. Commercial Cleaning Company Michigan

    ReplyDelete
  45. I appreciate of your work that shows in your content which is informative about carpet cleaning adelaide.

    ReplyDelete
  46. Thank you because you have been willing to share information with us. we will always appreciate all you have done here because I know you are very concerned with our. window cleaning honolulu

    ReplyDelete
  47. Nice post mate, keep up the great work, just shared this with my friendz Forto stroji za pometanje akcija

    ReplyDelete
  48. Nice reading, I love your content. This is really a fantastic and informative post. Keep it up. Opt for the reliable pest control services by professionals at top-pestcontrol.sg.

    ReplyDelete