As I’m working though my first MVC application I’ve run into a few problems with creating URLs based on ‘special characters’. Here’s a specific example, where I need to create links from a list of tags. In the example above a list like C#,ASP.NET,MVC is turned into a list of links that looks like this:
The tags are stored as a simple comma delimited string and are split and turned into links using the UrlHelper’s Action() method. The following is an extension method on my application specific UrlHelperExtensions class:
public static string GetTagLinkList(this UrlHelper urlHelper, string tags)
{
if (string.IsNullOrEmpty(tags))
return string.Empty;
string[] tagStrings = tags.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
StringBuilder html = new StringBuilder();
foreach (string tagString in tagStrings)
{
string urlAction = urlHelper.Action("List", "Snippet",
new {
listAction = "tag",
listFilter = tagString.Trim()
});
html.Append(StringUtils.Href(HttpUtility.HtmlEncode(tagString.Trim()), urlAction) + ", ");
}
if (html.Length > 2)
html.Length -= 2;
return html.ToString();
}
The Action() method works against the available routes for the application and makes it easy and consistent to link to other Urls in your applications without explicitly building up URLs which is nice. So rather than writing out the URL you can specify the controller and action, plus any additional parameters in the RouteValues parameter and the Action() method figures out based on the route configuration how the URL needs to be formatted.
This works great – until you hit certain special characters in your URL. Specifically in the tag list above one of the tags is C# – notice the hash tag. A hashtag has special meaning in a URL that is meant to jump to the name of an anchor contained within the content of the page.
So what happens above when the C# link is embedded? It gets embedded raw as http://codepaste.net/list/tag/C# without any encoding. This actually works, but only coincidentally – when the MVC application parses the URL to perform it’s routing the value for the language (C#) gets truncated to C because the # is treated as a URL artifact rather than part of the actual embedded tag. Bummer!
So my first thought was – well duh – I’ll just UrlEncode the value myself:
string urlAction = urlHelper.Action("List",
new {
listAction = "tag",
listFilter = urlHelper.Encode(tagString.Trim())
});
but that doesn’t actually work because Action() actually DOES encode values on its own. It just doesn’t encode # as it should. The result of the code above is
C%2523
which is double encoded C%23. Not quite what I had in mind.
This came up in a Twitter discussion and some argument ensued over whether the # should be encoded or not, but I’m pretty adamant in that it definitely should be: Although you may want a hashtag in a URL somewhere, the # has no place in an action parameter, so when Action() parses parameters it should most definitely UrlEncode() the entire value no exceptions.
In the end the only way I could figure out to make this work is fall back on manually creating the URL as a string:
string urlAction = urlHelper.Content("~/list/tag/") + urlHelper.Encode(tagString.Trim());
This correctly produces the following URL:
http://codepaste.net/list/tag/C%23
which works correctly although it doesn’t look very pretty.
The code above of course then also loses the ability to check routes and build the URL from the configured routes, so if routes are changed this code will have to be updated. OTOH, it’s probably more efficient to just write out the hardcoded string in this way. :-}
To Encode or Not To Encode
So, does this behavior of the Action() method strike anybody else as incorrect? I don’t see any reason that something like C# as an action parameter should ever not encode. While hashtags on URLs are certainly valid, they need to always be used at the end of a request so I can’t imagine a scenario where the # should be embedded as part of Action parameters. What do you think?
Other Posts you might also like