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?
-
\$\begingroup\$ This is what you are searching for: nuget.org/packages/LowercaseDashedRoute And read the one-line configuration here please: github.com/AtaS/lowercase-dashed-route \$\endgroup\$Ata S.– Ata S.2013年08月04日 16:52:21 +00:00Commented Aug 4, 2013 at 16:52
1 Answer 1
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)
-
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\$enamrik– enamrik2011年12月07日 17:06:53 +00:00Commented 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\$dav_i– dav_i2014年02月26日 15:04:39 +00:00Commented Feb 26, 2014 at 15:04