Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent URL-decoding of HttpRequest.Path in ASP.NET Core

HttpContext.Request.Path seems to decode percent-encoded URLs sometimes, but not all the time and I'm struggling to understand its behaviour. The documentation says nothing about encoding. I expect it to never decode the path, but testing on ASP.NET Core 3.1 I get results like this:

Request URL context.Request.Path Percent decoded?
http://localhost:5000/test%24 /test$ Yes
http://localhost:5000/test%24aa /test$aa Yes
http://localhost:5000/test%25a /test%25a No
http://localhost:5000/test%25aa /test%aa Yes
http://localhost:5000/test%20aa /test%20aa No

Is this a bug or is there some reason behind it?

Generated by creating an empty ASP.NET Core project in VS 2019 and changing the Startup class to

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.Use(async (context, next) =>
            {
                Console.WriteLine("Request path = " + context.Request.Path);
                await context.Response.WriteAsync("Request path = " + context.Request.Path);
            });
        }
    }

Program class unchanged from the template:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
    }

Edit: answer from an ASP.NET contributor: https://github.com/dotnet/aspnetcore/issues/30655

like image 481
EM0 Avatar asked Oct 17 '25 12:10

EM0


1 Answers

I had the same issue (with "%2B" corresponding to literal value "+") and I finally understood what happened.

  1. Note that context.Request.Path is of type PathString and has a Value property.

  2. The default Path.ToString() overload doesn't return the same value as Path.Value property:

    • Path.Value returns a partially* (see remark below) decoded URL which seems reliable (it is the real underlying/wrapped value).
    • Path.ToString() returns an encoded URL using the Path.Value property.
  3. Also note that PathString can be converted to string and used as is in a method with a string parameter, so becareful using the Value property when necessary!

To summarize

  • To have the decoded URL, use:
var decodedURL = context.Request.Path.Value;

Remark*: note that «decoded» means all characters with a "%" followed by two hexadecimal digits will be converted to the corresponding char, but the special char '+' will not. If you want the '+' char to be also converted, consider using the following:

var decodedURL = System.Net.WebUtility.UrlDecode(context.Request.Path.Value); //Including '+' char to space
  • To have the encoded URL, use:
var encodedURL = context.Request.Path.ToUriComponent();

Attention!

But beware, there is still an inconsistent use case due to remark* above.

Let's consider the example below:

Real resource path Encoded URL context.Request.Path.Value
/my+path example /my%2Bpath+example /my+path+example

As you can notice, if you encode URLs with '+' char for spaces and "%2B" for literal char '+' (which is legal), then it is impossible to rebuild the real resource path from the Path.Value...

To avoid this, I would recommend to encode spaces as "%20", but becareful once again: don't use System.Net.WebUtility.HtmlEncode neither System.Web.HttpUtility.UrlEncode (which will convert spaces to '+'), but System.Web.HttpUtility.UrlPathEncode.

As a small personal conclusion, there might be some good reasons (unknown to me) for the Path.Value property to be partially decoded, but in this case, the framework should provide another property to get the real raw/untouched URL.

Hope this will help!

Happy coding :)

like image 105
Kino101 Avatar answered Oct 20 '25 05:10

Kino101



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!