ASP.NET is 'normalizing' backslashes in requests paths to forward slashes, and I need them to come through as backslashes (it is used to perform a look-up in the database). I don't mind if escaped forward-slashes come through as unescaped, unlike this question.
config.Routes.MapHttpRoute(
name: "TransactionsApi",
routeTemplate: "api/transactions/{*transaction}",
defaults: new { controller = "transactions", transaction = RouteParameter.Optional }
);
Note that I have set up transaction to match the remainder of the path.
I tried the following URLs (both from a browser and Fiddler):
api/transactions/mscorlib.pdb\DFA83312EAB84F67BD225058B188F22B1\mscorlib.pdb
api/transactions/mscorlib.pdb\\DFA83312EAB84F67BD225058B188F22B1\\mscorlib.pdb
api/transactions/mscorlib.pdb%5CDFA83312EAB84F67BD225058B188F22B1%5Cmscorlib.pdb
api/transactions/mscorlib.pdb%5C%5CDFA83312EAB84F67BD225058B188F22B1%5C%5Cmscorlib.pdb
By the time they hit my Web API method they are all mscorlib.pdb/DFA83312EAB84F67BD225058B188F22B1/mscorlib.pdb
. I inspected the current HttpContext
and it looks like ASP.NET is doing this normalization (not MVC4).
Possible solutions:
{*transaction}
part if it contains a backslash. Not really address-bar hackable
Any idea on how to get ASP.NET to not do this normalization?
You cannot prevent this behavior as it is hard-coded into IIS.
I wanted to investigate this issue by decompiling the runtime and following the code. It's always nice to do that: you learn how the runtime works and sometimes you find the problem. Let's start the journey...
As a starting point, I'm decompiling System.Web with ILSpy, starting at the HttpRuntime class. Navigating through public static void ProcessRequest(HttpWorkerRequest wr)
, ProcessRequestNoDemand
, ProcessRequestNow
, ProcessRequestInternal
...
Here I want to investigate the following lines:httpContext = new HttpContext(wr, false);
,httpContext.Response.InitResponseWriter();
,httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);
.
In HttpContext.HttpContext(HttpWorkerRequest wr, bool initResponseWriter)
many things may cause this:this.Init(request, response)
,new HttpRequest(wr, this)
.
More precisely HttpContext.GetEurl()
(looks suspicious),Request.InternalRewritePath(VirtualPath.Create(virtualPath), null, true)
(safe),VirtualPath.Create(virtualPath)
(looks very suspicious),virtualPath = UrlPath.FixVirtualPathSlashes(virtualPath);
(the infamous!).
Let's write the stack trace that gets us here:
HttpRuntime.ProcessRequest...
(multiple methods)new HttpContext(wr, false)
this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
if (!string.IsNullOrEmpty(eurl))
(can we prevent entering the if?)this.Request.InternalRewritePath(VirtualPath.Create(virtualPath), null, true);
VirtualPath Create(string virtualPath)
unsafe static VirtualPath Create(string virtualPath, VirtualPathOptions options)
This last (unsafe) method is doing something to the path. First there is a loop over each character. If a char is below '.', and is different from '/' and is equal to '\', then flag = true
. After the loop, if (flag)
(src), then an exception may be thrown, and virtualPath = UrlPath.FixVirtualPathSlashes(virtualPath);
(src).
Is seems for now that nothing will help us avoid going there (maybe the eurl thing?).
The string FixVirtualPathSlashes(string virtualPath)
(src) replaces the backslashes into slashes and if removes the duplicate slashes. Shame.
What about the GetEurl
method? When you read src, you find that is will not help you.
The http runtime is killing your backslashes for no documented reason. There is no way you can disable this behavior.
Now, there must be a way. This guy referencing this page has a workaround. It seems that using the rewrite module, you can put the original URL back into the pipeline. I don't really like this solution because I don't know what's going on exactly. I have another idea...
I did not yet test this thing. Can you?
What if there was a place where the original request path was stored?
Searching HttpRequest
, none of the Url.OriginalString
, RawUrl
, Path
, ServerVariables
contains the desired value. Not even the _filePath
, _path
, _queryStringText
, _rawUrl
, _rewrittenUrl
, _url
private fields.
Search into the IIS7WorkerRequest
, the value is already changed at runtime. I suspect IIS is doing the thing before pushing the request to the ASP.NET runtime. It looks like there is no hope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With