11
\$\begingroup\$

I'm trying to build friendly URLs like /post/1/my-first-post. I started out with building my links like this:

@Html.ActionLink(Model.BlogPost.Title, "Index", "Post", new
{
 id = Model.BlogPost.Id,
 title = Model.FriendlyUrl
}, 
new { })

which were processed by this route:

routes.MapRoute(
 "Post", // Route name
 "posts/{id}/{title}", // URL with parameters
 new { controller = "Post", action = "Index" }, // Parameter defaults
 new { id = "^[0-9]+$" }
 );

When I create my view model, I would give it an IUrlNiceName which it used to create the output of FriendlyUrl:

public string FriendlyUrl
{
 get{ return UrlNiceName.ConvertToNiceName(BlogPost.Title); }
}

Then I felt like I didn't want to manage UrlNiceName-ing on the controller level so then I build a custom Route to do this for me:

public class NiceNameRoute : RouteBase
{
 private IUrlNiceNames UrlNiceNames { get; set; }
 private string UrlPrefix { get { return "post/"; } }
 public NiceNameRoute(IUrlNiceNames urlNiceNames)
 {
 UrlNiceNames = urlNiceNames;
 }
 public override RouteData GetRouteData(HttpContextBase httpContext)
 {
 RouteData result = null;
 string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
 //match e.g. post/1 or post/1/my-first-post
 Regex regex = new Regex(UrlPrefix + "[0-9]+($|/)");
 if (regex.Match(requestedURL).Success)
 {
 //get the start of the post id
 int prefixPos = requestedURL.IndexOf(UrlPrefix, StringComparison.OrdinalIgnoreCase);
 int startIdPos = prefixPos + UrlPrefix.Length;
 //get the end of the post id
 int endIdPos = requestedURL.IndexOf("/", startIdPos, StringComparison.OrdinalIgnoreCase);
 endIdPos = endIdPos == -1 ? requestedURL.Length : endIdPos;
 //get post id
 string id = requestedURL.Substring(startIdPos, endIdPos - startIdPos);
 result = new RouteData(this, new MvcRouteHandler());
 result.Values.Add("controller", "Post");
 result.Values.Add("action", "Index");
 result.Values.Add("id", id);
 }
 return result;
 }
 public override VirtualPathData GetVirtualPath(RequestContext requestContext,
 RouteValueDictionary values)
 {
 VirtualPathData result = null;
 if (values.ContainsKey("title")
 && Convert.ToString(values["action"]) == "Index"
 && Convert.ToString(values["controller"]) == "Post")
 {
 string niceName = UrlNiceNames.ConvertToNiceName(Convert.ToString(values["title"]));
 result = new VirtualPathData(this, UrlPrefix + Convert.ToString(values["id"]) + "/" + niceName);
 }
 return result;
 }
}

which I registered like this:

routes.Add(new NiceNameRoute(new HyphenUrlNiceNames()));

and generate links like this:

@Html.ActionLink(Model.BlogPost.Title, "Index", "Post", new
{
 id = Model.BlogPost.Id,
 title = Model.BlogPost.Title
}, 
new { })

I first wanted to take a crack at this but now I want to know how industry experts do it. Am I over engineering? Is there a simpler approach that is also elegant? Are there any issues with my custom Route?

RubberDuck
31.1k6 gold badges73 silver badges176 bronze badges
asked Dec 5, 2011 at 5:32
\$\endgroup\$
1

1 Answer 1

8
\$\begingroup\$

Personally I think you are over thinking it. Why not just create a string extension method called FriendlyString or PrepareForUrl and then use the original route

@Html.ActionLink(Model.BlogPost.Title, "Index", "Post", new
{
 id = Model.BlogPost.Id,
 title = Model.BlogPost.Title.FriendlyString()
}, null)
answered Dec 6, 2011 at 16:04
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Thank you. I WAS beginning to feel like I was making a mountain out of a mole hill. After reviewing your simple answer I realized that my simple goal did not require the complexity I was forcing unto it. And after enjoying making a custom route, I don't think it helped with my goal at all. Now I'm wondering how I got that far. I need to learn to catch waste-of-time stuff like this earlier. \$\endgroup\$ Commented Dec 7, 2011 at 17:06
  • \$\begingroup\$ @enamrik I actually think your way is better. Yes it is more work but has a better separation of concerns - your View shouldn't have knowledge of how the routes are structured, which it does if you use this method. \$\endgroup\$ Commented Feb 26, 2014 at 15:04

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.