Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Url.Action() and RouteValue Encoding


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:

Tag List

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?

Make Donation
Posted in MVC   ASP.NET  


Feedback for this Post

 
# Url.Action() and RouteValue Encoding
by DotNetKicks.com July 19, 2009 @ 5:50pm
You've been kicked (a good thing) - Trackback from DotNetKicks.com
# re: Url.Action() and RouteValue Encoding
by Haacked July 19, 2009 @ 8:26pm
I think it should be encoded as %23. the reason it isn't is because we rely on a method of the URL class for the encoding that might be the wrong method to call in this context. I believe we'll fix this in ASP.NET MVC 2.
# Interesting Finds: July 20, 2009
by Jason Haley July 20, 2009 @ 4:07am
Interesting Finds: July 20, 2009
# re: Url.Action() and RouteValue Encoding
by Duncan Smart July 20, 2009 @ 5:15am
My hunch is that somewhere, something is calling Uri.ToString() and not Uri.AbsoluteUri, which is something that bit us ages ago. For some reason Uri.ToString outputs an unescaped ("human readable"?) version of the querystring, thereby decoding carefully URL-encoded values.
# re: Url.Action() and RouteValue Encoding
by Richard Deeming July 20, 2009 @ 10:23am
Actually, it's more likely that the Bind method in the internal ParsedRoute class is calling Uri.EscapeUriString [1] instead of Uri.EscapeDataString [2]; the former is used to escape an entire URI, whereas the latter is used to escape part of a URI. This is similar to the encodeURI and encodeURIComponent methods in JavaScript.

For example:

Console.WriteLine(Uri.EscapeUriString("C++/C#"));
// C++/C#

Console.WriteLine(Uri.EscapeDataString("C++/C#"));
// C%2B%2B%2FC%23


It's good to hear that this will probably be fixed in v2.


[1] http://msdn.microsoft.com/en-us/library/system.uri.escapeuristring.aspx
[2] http://msdn.microsoft.com/en-us/library/system.uri.escapedatastring.aspx
# re: Url.Action() and RouteValue Encoding
by Khalid Abuhakmeh July 20, 2009 @ 5:25pm
instead of trying to encode the # in C# why don't you just interpret # to the word "sharp".

so your web address wouldn't look like

http://codepaste.net/list/tag/C%23

but instead

http://codepaste.net/list/tag/Csharp

do the same for any other special character. "+" is "plus," "-" is "minus" or "dash", and "=" is "equal(s)." I feel preserving the web address readability is completely worth the effort of writing an interpreter.
# re: Url.Action() and RouteValue Encoding
by Rick Strahl July 20, 2009 @ 5:44pm
@Khalid - not sure if that really helps. While it may make the URL more readable (which doesn't really matter), it doesn't make the URL more hackable to a power user. A power user would expect C# to work so they'd likely try http://codepaste.net/list/tag/C# and end up with the same original problem.

Like the idea of translating parameters though!
# re: Url.Action() and RouteValue Encoding
by Chris Love July 23, 2009 @ 5:05am
This is not really SEO freindly. First 'normal' users are not going to understand a URL Encoded character. Second from what I know search engines are not going to take the time to translate the %23 to #. If you think about it they cannot just assume something has actually been encoded. I like your example because it really shows this problem I have been raising for several months now. I typically use an 's to break the routing.

I do hope to create a blog in the coming days to show my position on this issue and it goes beyond just the Routing system we now have, it really affects any URL rewriting solution that solely relies on regular expressions. The real issue is how to rehydrate to the real URL correctly, while making a truely friendly URL that gets the highest SEO juice you can.
# Url.Action() and RouteValue Encoding - Rick Strahl
by DotNetShoutout July 28, 2009 @ 9:21am
Thank you for submitting this cool story - Trackback from DotNetShoutout
# re: Url.Action() and RouteValue Encoding
by Boersnoes August 03, 2009 @ 2:44am
I too encountered this problem a while ago.
ref: http://stackoverflow.com/questions/240224/double-incomplete-parameter-url-encoding
It's good to read that the team is on it and it will be fixed in the 2.0 version.
# re: Url.Action() and RouteValue Encoding
by Krisztian March 18, 2013 @ 4:38am
It looks like that as of MVC 4.0 this is still not changed. Some characters are still not encoded using Url.Action(...).
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014