Prevent static file handler from intercepting filename-like URL - asp.net-mvc

In my web application I have a route which looks like this:
routeCollection.MapRoute(
"AdfsMetadata", // name
"FederationMetadata/2007-06/FederationMetadata.xml", // url
new { controller = "AdfsController", action = "MetaData" }); // defaults
The idea behind this route was to work better with Microsoft AD FS server (2.0+) which looks for AD FS metadata at this point when you just specify a host name. With MVC3 all worked fine. But we upgraded the project to MVC4 recently and now the call for this URL results in a 404, the handler mentioned on the error page is StaticFile and the physical path is D:\path\to\my\project\FederationMetadata\2007-06\FederationMetadata.xml. I assume that MVC or ASP.NET "thinks" it must be a request for a static file and looks for the file, but it isn't a file. The data is generated dynamically - that's why I routed the URL to a controller action. The problem is that even the Route Debugger by Phil Haack doesn't work. It's just a 404 with no further information besides that IIS tried to access a physical file which isn't there.
Does anyone have a solution for this? I just want this URL to be routed to a controller action.
P.S.: I'm not 100% sure that the cause was the upgrade to MVC4, it was just a guess because the error occurred at about the same time as the upgrade, and the same route works in another project which is still using MVC3.
Edit:
I have a custom ControllerFactory which needs the full class name (AdfsController instead of Adfs), so the suffix Controller is correct in this case.

Add the following to the <handlers> section of the <system.webServer> node:
<add
name="AdfsMetadata"
path="/FederationMetadata/2007-06/FederationMetadata.xml"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
This instructs IIS that GET requests to the specified url should be handled by the managed pipeline and not considered as static files and served by IIS directly.
I have a custom ControllerFactory which needs the full class name
(AdfsController instead of Adfs), so the suffix Controller is correct
in this case.
Double check this please. Try with and without the Controller suffix in the route definition.

IIS by default is serving that file as static withouth going into ASP pipeline, can you change the route to not have a .xml extension (which is not necessary at least you have a specific requirement about that)
You can specifiy the route like this:
routeCollection.MapRoute(
"AdfsMetadata", // name
"FederationMetadata/2007-06/FederationMetadata", // url
new { controller = "AdfsController", action = "MetaData" }); // defaults
Other solution would be to add to your web.config
<modules runAllManagedModulesForAllRequests="true">
but I recommend you to avoid this as possible since what it does is to stop IIS from serving all static files
EDIT Last try... a custom http handler for that specific path would work. This post is similar to your problem only that with images (look for "Better: Custom HttpHandlers" section)

Related

What is wrong with this MVC route?

I want to create a custom image URL handler, which can handle GET requests with the following URL pattern:
http://blah.com/images/image1.gif
To handle this I created the following route:
routes.MapRoute(
name: "appImages",
url: "images/{*imageName}",
defaults: new { controller = "Image", action = "Grab" });
This does not seem to work even for images with no spaces in their names. What am I doing wrong?
By default, IIS looks for existing files and folders when it processes the request from the user. If you have an existing folder or file, then IIS will simply try and retrieve the file from disk rather than passing the request on to the .NET pipeline / routing engine.
Check to see if you have a folder already called images in your solution. If you do have this folder, I suggest (the easiest method) either changing your controller name or the existing folder name in order to resolve your issues.

Trailing dot in MVC 5 WebRequest URL causes 404

I am running MVC 5 and have a search API that produces the following link: /SearchedItem.?format=json where SearchedItem. is the user's input into search. This obviously causes a famous 404 due to a dot character. I've looked into all of the following solutions:
Dot character '.' in MVC Web API 2 for request such as api/people/STAFF.45287
Dots in URL causes 404 with ASP.NET mvc and IIS
ApiController returns 404 when ID contains period
However, neither adding a slash (tried both /SearchedItem./?format=json and /SearchedItem.?format=json/) nor RAMMFAR worked.
Looking for any new suggestions.
You have to change your web.config, the trailing dot let's iis think you are accessing an image.
Add the following within the system.webServer / handlers ( web.config)
<add name="ApiURIs-ISAPI-Integrated-4.0"
path="/api/*"
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
Another suggest would be to set RunAllManagedModulesForAllRequests to true, but i wouldn't recommend that. All static assets would be handled through the .net code then :)
I see in your question that you checked related links. But are you sure? Because i have came accross this in the past and above was my solution...
Most suitable way is to encode once you route to url and decode in the respective action you can use HttpUtility to perform encoding and decoding
If you don't want to encode and decode then try adding relaxedUrlToFileSystemMapping config as explained Here

URLs with several path components get sent directly to static file handler

I have the following problem with an Asp.net MVC application when running on IIS (tested with version 7.0 and 8.5) : if the path in the URL has no more than 3 components (for example http://myhost.com/a/b/c, it gets correctly handled by the application code. But when an additional component is added in the path (http://myhost.com/a/b/c/d), it will be sent directly to the static file handler. As the path is not used for a physical file, I get a 404 (with the error page coming from the static file handler, not the custon 404 page configured in web.config)
I don't have this problem on my dev station running IIS Express.
I use runAllManagedModulesForAllRequests="true" in web.config, disabling it doesn't solve the issue.
Disabling the static file handler doesn't help, I still get a 404 but this time the error message says "Handler Not yet determined".
I really looks this is something happening very early in IIS processing, because if I recycle the application pool and try to access to this URL, the response comes immediately, without the usual delay that I get when the application needs to start.
What am I missing? Why is IIS not forwarding such requests to my application?
It's your routes!!
The default route in MVC only takes over controller, action & ID. You're trying to use a fourth which you haven't defined. Just add another url parameter to your route collection.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{another}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, another= UrlParameter.Optional }
);
Hope this helps.
I think I found something: the long url was not handled by an MVC route, but by an OWIN middleware. Somehow, it seems that for IIS, if no route is configured for an URL, then the request pipeline is completely ignored, including any OWIN middlewares (and also custom page for 404).
By adding another parameter in a route like suggested by heymega, I was able to work around the problem, the OWIN middlewares were executed. I added the following route:
routes.MapRoute("CatchLongUrls", "{a}/{b}/{c}/{*d}",
new {controller = "Invalid", action = "Nothing"});

ServiceStack DefaultRedirectPath not triggering

I'm using ServiceStack in an MVC4, VS2012 project running on windows7. I'm attempting to call a default "/search" route when the application loads. To do this I have the following code in my AppHost Configure method
Plugins.Add(new RazorFormat());
SetConfig(new EndpointHostConfig
{
ServiceStackHandlerFactoryPath = "api",
DefaultRedirectPath = "/search"
});
If I call this route directly (via /api/search) it works correctly. However when I run my project I simply get a HTTP Error 403.14 - Forbidden error. It's appears to be attempting to locate a static source document from the website root (I've removed all of these) rather than the dynamic route specified in DefaultRedirectPath
I've also added a HttpHandler via the CatchAllHandlers method to see if the route is being attempted but it appears the DefaultRedirect is simply not happening.
Any suggestions would be greatly appreciated. From everything I've read this should just work.
I'm assuming that you've set up your /api path correctly in web.config too.
DefaultRedirectPath is just a simple redirect used when you request the root of your API (i.e. "/api"). It literally returns a 302 with your DefaultRedirectPath as the Location header (combined with the application root URL, if your ASP.NET application isn't at the root of the server). In other words, it's not a "route", just a relative URL. And in your case, it will redirect to /search in the root of your application, not to /api/search.
It should work, if you use DefaultRedirectPath = "/api/search".
However, when ServiceStack isn't at the root of the website, it will use MetadataRedirectPath first, and only if that is null or empty, DefaultRedirectPath. So you'll need to set MetadataRedirectPath to null, if this is what you want.
As for your test with CatchAllHandlers, as far as I can tell, CatchAllHandlers will actually cause your DefaultRedirect to not be used - the CatchAllHandler will be used as a handler, and the DefaultRedirectHandler that does the DefaultRedirectPath redirect won't ever come into play.
That will take care of redirecting from "/api" to "/api/search".
MVC is in control of your root url - the "api" addition to web.config and AppHost.Config does exactly that - keep ServiceStack only in control of "/api", while letting MVC take care of the rest. So, with that setup, if you want a redirect from "/" to "/api/search", you'd need to do it in the MVC home controller.

ASP.NET MVC, Url Routing: Maximum Path (URL) Length

The Scenario
I have an application where we took the good old query string URL structure:
?x=1&y=2&z=3&a=4&b=5&c=6
and changed it into a path structure:
/x/1/y/2/z/3/a/4/b/5/c/6
We're using ASP.NET MVC and (naturally) ASP.NET routing.
The Problem
The problem is that our parameters are dynamic, and there is (theoretically) no limit to the amount of parameters that we need to accommodate for.
This is all fine until we got hit by the following train:
HTTP Error 400.0 - Bad Request
ASP.NET detected invalid characters in the URL.
IIS would throw this error when our URL got past a certain length.
The Nitty Gritty
Here's what we found out:
This is not an IIS problem
IIS does have a max path length limit, but the above error is not this.
Learn dot iis dot net
How to Use Request Filtering
Section "Filter Based on Request Limits"
If the path was too long for IIS, it would throw a 404.14, not a 400.0.
Besides, the IIS max path (and query) length are configurable:
<requestLimits
maxAllowedContentLength="30000000"
maxUrl="260"
maxQueryString="25"
/>
This is an ASP.NET Problem
After some poking around:
IIS Forums
Thread: ASP.NET 2.0 maximum URL length?
http://forums.iis.net/t/1105360.aspx
it turns out that this is an ASP.NET (well, .NET really) problem.
The heart of the matter is that, as far as I can tell, ASP.NET cannot handle paths longer than 260 characters.
The nail in the coffin in that this is confirmed by Phil the Haack himself:
Stack Overflow
ASP.NET url MAX_PATH limit
Question ID 265251
The Question
So what's the question?
The question is, how big of a limitation is this?
For my app, it's a deal killer. For most apps, it's probably a non-issue.
What about disclosure? No where where ASP.NET Routing is mentioned have I ever heard a peep about this limitation. The fact that ASP.NET MVC uses ASP.NET routing makes the impact of this even bigger.
What do you think?
I ended up using the following in the web.config to solve this problem using Mvc2 and .Net Framework 4.0
<httpRuntime maxUrlLength="1000" relaxedUrlToFileSystemMapping="true" />
Http.sys service is coded with default maximum of 260 characters per Url segment.
An "Url segment" in this context is the content between "/" characters in the Url. For example:
http://www.example.com/segment-one/segment-two/segment-three
The max allowed Url segment length can be changed with registry settings:
Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters
Value: UrlSegmentMaxLength
Type: REG_DWORD
Data: (Your desired new Url segment maximum allowed length, e.g. 4096)
More about http.sys settings:
http://support.microsoft.com/kb/820129
The maximum allowed value is 32766. If a larger value is specified, it will be ignored. (Credit: Juan Mendes)
Restarting the PC is required to make a change to this setting take effect. (Credit: David Rettenbacher, Juan Mendes)
To solve this, do this:
In the root web.config for your project, under the system.web node:
<system.web>
<httpRuntime maxUrlLength="10999" maxQueryStringLength="2097151" />
...
In addition, I had to add this under the system.webServer node or I got a security error for my long query strings:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxUrl="10999" maxQueryString="2097151" />
</requestFiltering>
</security>
...
OK so part of the reason I posted this was also because we have found a work around.
I hope this will be useful to someone in the future :D
The workaround
The workaround is quite simple, and it's quite nice too.
Since we know which parts of the site will need to use dynamic parameters (and hence will have a dynamic path and length), we can avoid sending this long url to ASP.NET routing by intercepting it before it even hits ASP.NET
Enter IIS7 Url Rewriting (or any equivalent rewrite module).
We set up a rule like this:
<rewrite>
<rules>
<rule>
<rule name="Remove Category Request Parameters From Url">
<match url="^category/(\d+)/{0,1}(.*)$" />
<action type="Rewrite" url="category/{R:1}" />
</rule>
</rules>
</rewrite>
Basically, what we're doing is just keeping enough of the path to be able to call the correct route downstream. The rest of the URL path we are hacking off.
Where does the rest of the URL go?
Well, when a rewrite rule is fired, the IIS7 URL Rewrite module automagically sets this header in the request:
HTTP_X_ORIGINAL_URL
Downstream, in the part of the app that parses the dynamic path, instead of looking at the path:
HttpContext.Request.Url.PathAndQuery
we look at that header instead:
HttpContext.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]
Problem solved... almost!
The Snags
Accessing the Header
In case you need to know, to access the IIS7 Rewrite Module header, you can do so in two ways:
HttpContext.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]
or
HttpContext.Request.Headers["X-ORIGINAL-URL"]
Fixing Relative Paths
What you will also notice is that, with the above setup, all relative paths break (URLs that were defined with a "~").
This includes URLs defined with the ASP.NET MVC HtmlHelper and UrlHelper methods (like Url.Route("Bla")).
This is where access to the ASP.NET MVC code is awesome.
In the System.Web.Mvc.PathHelper.GenerateClientUrlInternal() method, there is a check being made to see if the same URL Rewrite module header exists (see above):
// we only want to manipulate the path if URL rewriting is active, else we risk breaking the generated URL
NameValueCollection serverVars = httpContext.Request.ServerVariables;
bool urlRewriterIsEnabled = (serverVars != null && serverVars[_urlRewriterServerVar] != null);
if (!urlRewriterIsEnabled) {
return contentPath;
}
If it does, some work is done to preserve the originating URL.
In our case, since we are not using URL rewriting in the "normal" way, we want to short circuit this process.
We want to pretend like no URL rewriting happened, since we don't want relative paths to be considered in the context of the original URL.
The simplest hack that I could think of was to remove that server variable completely, so ASP.NET MVC would not find it:
protected void Application_BeginRequest()
{
string iis7UrlRewriteServerVariable = "HTTP_X_ORIGINAL_URL";
string headerValue = Request.ServerVariables[iis7UrlRewriteServerVariable];
if (String.IsNullOrEmpty(headerValue) == false)
{
Request.ServerVariables.Remove(iis7UrlRewriteServerVariable);
Context.Items.Add(iis7UrlRewriteServerVariable, headerValue);
}
}
(Note that, in the above method, I'm removing the header from Request.ServerVariables but still retaining it, stashing it in Context.Items. The reason for this is that I need access to the header value later on in the request pipe.)
Hope this helps!
I was having a similar max URL length issue using ASP.NET Web API 4, which generated a slightly different error:
404 Error
The fix for me was described above by updating the Web.config with BOTH of the following tags:
<system.web>
<httpRuntime maxUrlLength="10999" maxQueryStringLength="2097151" />
and
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxUrl="10999" maxQueryString="2097151" />
</requestFiltering>
</security>
I think you're trying to hard to use GET. Try changing the request method to POST and put those query string parameters into the request body.
Long URL does not help SEO as well, does it?
It appears that the hard-coded max URL length has been fixed in .NET 4.0. In particular, there is now a web.config section with:
<httpRuntime maxRequestPathLength="260" maxQueryStringLength="2048" />
that let you expand the range of allowed URLs.

Resources