61

My Map is:

routes.MapRoute(
   "Default",                                             // Route name
   "{controller}/{action}/{id}",                          // URL with params
   new { controller = "Home", action = "Index", id = "" } // Param defaults
);

If I use the URL http://localhost:5000/Home/About/100%2f200 there is no matching route. I change the URL to http://localhost:5000/Home/About/100 then the route is matched again.

Is there any easy way to work with parameters that contain slashes? Other escaped values (space %20) seem to work.

EDIT:

To encode Base64 works for me. It makes the URL ugly, but that's OK for now.

public class UrlEncoder
{ 
    public string URLDecode(string  decode)
    {
        if (decode == null) return null;
        if (decode.StartsWith("="))
        {
            return FromBase64(decode.TrimStart('='));
        }
        else
        {
            return HttpUtility.UrlDecode( decode) ;
        }
    }

    public string UrlEncode(string encode)
    {
        if (encode == null) return null;
        string encoded = HttpUtility.PathEncode(encode);
        if (encoded.Replace("%20", "") == encode.Replace(" ", ""))
        {
            return encoded;
        }
        else
        {
            return "=" + ToBase64(encode);
        }
    }

    public string ToBase64(string encode)
    {
        Byte[] btByteArray = null;
        UTF8Encoding encoding = new UTF8Encoding();
        btByteArray = encoding.GetBytes(encode);
        string sResult = System.Convert.ToBase64String(btByteArray, 0, btByteArray.Length);
        sResult = sResult.Replace("+", "-").Replace("/", "_");
        return sResult;
    }

    public string FromBase64(string decode)
    {
        decode = decode.Replace("-", "+").Replace("_", "/");
        UTF8Encoding encoding = new UTF8Encoding();
        return encoding.GetString(Convert.FromBase64String(decode));
    }
}

EDIT1:

At the end it turned out that the best way was to save a nicely formated string for each item I need to select. Thats much better because now I only encode values and never decode them. All special characters become "-". A lot of my db-tables now have this additional column "URL". The data is pretty stable, thats why I can go this way. I can even check, if the data in "URL" is unique.

EDIT2:

Also watch out for space character. It looks ok on VS integrated webserver but is different on iis7 Properly url encode space character

Community
  • 1
  • 1
Mathias F
  • 15,906
  • 22
  • 89
  • 159
  • 2
    You could also come up with some other way to mask the slash, say, replace it with something else by convention. I know. That's ugly as well, but at least the URL stays somewhat readable. – Tomalak Feb 26 '09 at 20:10
  • 2
    I noticed that forward slashes and dots give me errors. I made a quick helper that replaces them with "-slash-" and "-dot-". Wonder why the regular Url.Encode/Decode don't work something out. Also, why would an escaped character be giving any errors? – Boris Callens Mar 23 '09 at 08:06
  • 7
    This isn't an encoding issue with routing; it's apparently a bug in the .NET Uri class. According to [my reading of] the URI RFC, encoded slashes in the path should not be considered segment separators. MVC Routing doesn't have a chance to get it right because the Uri class (incorrectly) decodes the slashes before routing even sees it. See section 2.2 and 2.4 of the RFC. http://labs.apache.org/webarch/uri/rfc/rfc3986.html#reserved – Andrew Arnott Nov 15 '09 at 03:43
  • What happens if the original string contained a "-" or a "_"? – juan Dec 03 '09 at 19:31
  • http://gathadams.com/2009/01/06/allowing-special-characters-forward-slash-hash-asterisk-etc-in-aspnet-mvc-urls/ gives a "404 - file not found" error. – xraminx Apr 22 '09 at 16:41
  • Gath Adams recommends Base64 encoding on any parameters that can contain slashes. He also explains the issue in more detail: Blog entry: http://gathadams.com/2009/01/06/allowing-special-characters-forward-slash-hash-asterisk-etc-in-aspnet-mvc-urls/ – Tomalak Feb 26 '09 at 18:10
  • 2
    Whoa, there partner! Base64 encoding includes the slash character too! That's not a solution you can rely on for this problem. – Andrew Arnott Nov 14 '09 at 18:52
  • This is correct. I did not think of that. – Tomalak Dec 03 '09 at 21:46
  • Which character when encoded has a slash? – Mathias F Jun 22 '12 at 21:29
  • as mentioned in url slash is valid base64 character so this not helps – Yauhen.F Feb 25 '13 at 09:05
  • @Yauhen You are right, as acknowledged in my older comment above. The OP should not have accepted this answer. To remedy this, the output of vanilla Base64 encoding could be fixed by replacing `+` and `/` with `-` and `_`, respectively (and back again at the receiving end). This is a well-known, safe technique (the Base64 alphabet itself does not contain `-` and `_`) and a cheap operation at that. This means existing encoder libraries still could be used. – Tomalak Feb 25 '13 at 09:53

10 Answers10

49

If it's only your last parameter, you could do:

routes.MapRoute(
    "Default",                                                // Route name
    "{controller}/{action}/{*id}",                            // URL with parameters
    new { controller = "Home", action = "Index", id = "" });  // Parameter defaults
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • I am not comfortable with doing this as a general practice, especially on a public-facing website, but I did do this on an intranet webapp and don't feel too guilty about it. Thanks for pointing this solution out! – jkade Jun 19 '12 at 19:46
  • 7
    @jkade (or anyone) why are you not comfortable with this on a public facing website? – wal Sep 17 '12 at 11:24
  • 2
    For those who only want it for a single route, you can do the same via RouteAttribute to your controller's action, e.g. [Route("{*id}")] – Vitaly Jul 27 '16 at 14:34
  • 1
    This literally does nothing to help with %5C – JoyalToTheWorld Oct 06 '16 at 18:38
26

Here's a simple explanation of the solution and a summation of what has already been said.

Request side:

  1. UrlEncode your path.
  2. Replace the '%' with '!'.
  3. Make the request.

Response side:

  1. Replace the '!' with '%'.
  2. UrlDecode your path.
  3. Use the parameters as they were intended.

Rinse, repeat, enjoy.

Don Rolling
  • 2,301
  • 4
  • 30
  • 27
24

In .NET 4.0 beta 2, the CLR team has offered a workaround.

Add this to your web.config file:

<uri> 
    <schemeSettings>
        <add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
    </schemeSettings>
</uri>

This causes the Uri class to behave according to the RFC describing URIs, allowing for slashes to be escaped in the path without being unescaped. The CLR team reports they deviate from the spec for security reasons, and setting this in your .config file basically makes you take ownership of the additional security considerations involved in not unescaping the slashes.

Andrew Arnott
  • 80,040
  • 26
  • 132
  • 171
  • This sounds great. I will use this as the answer once .NET 4.0 is released. – Mathias F Nov 17 '09 at 14:40
  • 4
    This doesn't work. You get blue underlines in Visual Studio and it seems to have no effect. – cdmckay Jun 22 '11 at 01:40
  • 12
    Note: As of the official release of .NET 4, [MSDN](http://msdn.microsoft.com/en-us/library/ee656542.aspx) states that this setting can only be added to the machine.config or application.config - NOT the web.config. – Stelloy Dec 08 '11 at 13:57
  • 1
    Years and years later, still an issue with the .NET Framework 4.7.1 and the Uri class. Tks a lot. This is a very obscure behavior for me. Note that for class libraries projects this problem does not occur. – Lorenzo Solano Martinez Sep 14 '18 at 13:17
  • @StephenLloyd this worked for me in the web.config (ASP.NET app using the .NET Framework classic 4.7.1). – Lorenzo Solano Martinez Sep 14 '18 at 13:33
14

One other option is to use a querystring value. Very lame, but simpler than custom encoding.

http://localhost:5000/Home/About?100%2f200
Jon Galloway
  • 52,327
  • 25
  • 125
  • 193
  • Good call, Jon. This is the only safe way I can come up with. It breaks convention a bit, but solves the problem when your ID has to be a string of any characters. – Brian Genisio Jan 28 '13 at 15:10
  • 1
    I don't think this is lame - I think it's the best option - I think your example is wrong though? Should be - /About?x=100%2f200 - no?? – niico Apr 13 '17 at 09:00
9

Same for Java / Tomcat.

There is still a problem if you have got an encoded "/" (%2F) in your URL.

RFC 3986 - Section 2.2 says: "If data for a URI component would conflict with a reserved character's purpose as a delimiter, then the conflicting data must be percent-encoded before the URI is formed." (RFC 3986 - Section 2.2)

But there is an Issue with Tomcat:

http://tomcat.apache.org/security-6.html - Fixed in Apache Tomcat 6.0.10

important: Directory traversal CVE-2007-0450

Tomcat permits '\', '%2F' and '%5C' [...] .

The following Java system properties have been added to Tomcat to provide additional control of the handling of path delimiters in URLs (both options default to false):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true|false
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: true|false

Due to the impossibility to guarantee that all URLs are handled by Tomcat as they are in proxy servers, Tomcat should always be secured as if no proxy restricting context access was used.

Affects: 6.0.0-6.0.9

So if you have got an URL with the %2F character, Tomcat returns: "400 Invalid URI: noSlash"

You can switch of the bugfix in the Tomcat startup script:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
simonox
  • 559
  • 6
  • 6
1

You can avoid the double encoding/decoding suggestions above and simply use HttpServerUtility.UrlTokenEncode and the corresponding UrlTokenDecode.

1

For inbound encoded '/' issue, I was able to fix my issue by adding '*' to catchall the id parameter and then was able to passing an encoded '/' into the the control correctly (the parameter was a string with an encoded '/')

routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{*id}",
            defaults: new 
            { 
                controller = "Control", 
                action = "Action", 
                id = UrlParameter.Optional 
            })
Martin
  • 3,096
  • 1
  • 26
  • 46
0

That's interesting about .NET 4. Anyway, this link describes RFC 1738 and includes which characters need encoding and which are just "unsafe". link text

If I want an SEO friendly URL, (like when you want to put a forum post subject in the URL), is skip encoding and replace anything that's not A-Z, a-z, 0-9.

public static string CreateSubjectSEO(string str)
    {
        int ci;
        char[] arr = str.ToCharArray();
        for (int i = 0; i < arr.Length; i++)
        {
            ci = Convert.ToInt32(arr[i]);
            if (!((ci > 47 && ci < 58) || (ci > 64 && ci < 91) || (ci > 96 && ci < 123)))
            {
                arr[i] = '-';
            }
        }
        return new string(arr);
    }
BillB
  • 404
  • 1
  • 4
  • 11
-1

As suggested here when the problem was faced by Symfony 1.x developers (+ suggested in PHP comments for urlencode()):

  • Encode '/' to '%2F' before urlencode()
  • Decode '%2F' to '/' after (if necessary) urldecode()

Note: you can use rawurlencode(), but you will still have to urlencode '/' twice.

Advantages:

  • Avoids the need of additional escaping processes (if replacing '/' with a special character like '!' or '_')
  • Do not relies on any server setting such as AllowEncodedSlashes for Apache
Maxime Pacary
  • 22,336
  • 11
  • 85
  • 113
  • This doesn't work with ASP.NET 4. It still says HTTP Error 400 - Bad Request. – Rn222 Nov 13 '12 at 14:45
  • Sorry if this answer is intended for PHP/Apache. I guess that the same kind of logic should apply for ASP.NET. Maybe someone will have time to "translate" it for ASP.NET before me. I leave my answer as I think it can help, even if the technologies used are different (like @simonox answer) – Maxime Pacary Jun 10 '15 at 22:47
-3

Just use Server.UrlDecode. It will work, I've tested.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
shijas km
  • 11
  • 3
  • UrlDecoding is not the problem here, it is the routing within MVC and the fact that encoded strings with slashes (%2f) will be parsed in the routing as if they are (%2f) are part of the url. – Rodi Jan 22 '14 at 10:36