Configure ASP.NET Managed Handler to handle -all- requests - c#

I have a simple objective; I just want -every- single 404 error to redirect to a Single-Page Application for the SPA to handle routing.
This seems to work for routes that could possibly exist within the ASP.NET app (based on existing Controllers). However, if I navigate to a route that couldn't not possibly have been an ASP.NET route (which is happening, as some routes only exist client-side in the Vue app), it seems like IIS is immediately taking charge of the request and issuing this simple text/html response:
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
The response is issued almost immediately from receipt of the request, which seems to indicate it is being immediately handled as a 404 by IIS before any .NET managed handlers touch it.
How do I stop IIS from doing this?
Additional Info:
ASP.NET Web.config error fallback is already in place:
<system.webServer>
<httpErrors>
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath="" path="/vue-fallback.html" responseMode="File" />
</httpErrors>
</system.webServer>
This is an ASP.NET MVC Server; System.Web.Mvc v4.0.0.0.
IIS Version: IIS 8
ETA:
I ran the server locally (not sure why I didn't do this sooner), the exact error seems to be a failure in the StaticFile handler.
HTTP Error 404.0 - Not Found
...
Module IIS Web Core
Notification MapRequestHandler
Handler StaticFile
Error Code 0x80070002

This seems to solve my issue:
routes.MapRoute(
"Vue-Fallback",
"{*catchall}",
new { controller = "Home", action = "VueFallback" }
);

Related

Custom Error Page handled by Application when file not found

In asp.net mvc is it possible to redirect to a custom error page when a static file is is not found rather than have IIS serve an error page?
The example below, adding this to System.Web in the web.config file will redirect the user to an Error controller and return an Error page view.
<customErrors mode="RemoteOnly" defaultRedirect="~/Error" redirectMode="ResponseRedirect">
<error statusCode="404" redirect="~/Error" />
</customErrors>
That is fine if the url resembels a controller and action but if a static file was typed into the url such as mysite.com/myFile.html, the custom error page isn't returned and IIS serves an Error page. Is it possible to have this handled within the mvc application, perhaps by redirecting the user to the same Error controller that the above custom error handler redirects to?

How can I elegantly handle server error where the database is unavailable in an ASP.NET application?

I have an ASP.NET web application. I've set up IIS 8.5 to handle various HTTP error responses using static HTML error page e.g.
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="500" subStatusCode="-1" />
...
<error statusCode="500" prefixLanguageFilePath="httpErrors\custom" path="500.htm" responseMode="File" />
...
</httpErrors>
</system.webServer>
I've also set my application to delegate exception handling to IIS because I want to be sure any exceptions which cause the application to fail are still handled elegantly. Therefore I've set my web.config as:
<system.web>
...
<customErrors defaultRedirect="~/CustomError.aspx" mode="On" />
...
</system.web>
CustomError.aspx just looks like this:
<%# Page Language="C#" %>
<script runat="server">
protected void Page_Init(object sender, EventArgs e)
{
Response.TrySkipIisCustomErrors = false;
Response.StatusCode = 500;
}
</script>
I'm trying to tell my application to defer to IIS for error handling.
If I now simulate a server error by throwing an exception I see my request is redirected to /CustomError.aspx?aspxerrorpath=/path/to/fracture.aspx. In this case the application's customErrors setting has kicked in presumably because the exception has been thrown in ASP.NET. However, what I see is my 500.htm page configured in httpErrors (see above).
If I simulate a server error a different way by simply setting Response.StatusCode = 500; then this time I am not redirected but I still see the contents of my 500.htm error page. Presumably this time IIS is handling the server error without the application's involvement.
Both these things are good :)
However, now I want to see what happens if my database goes down. To simulate this I change my connection string to point to a database which doesn't exist. Which of the above scenarios do you think I observe? Neither! What I actually see is a very ugly:
Server Error in '/template' Application.
Runtime Error
Description: An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.
I can see in the browser address bar that I've been redirected to CustomError.aspx so the application has handled the error as expected. You can see from the code of CustomError.aspx above that it makes no database calls. In fact it barely does anything. So why am I not seeing my nice friendly error page? I can even see from monitoring the request that it's still a 500 status code.
A lot of detail I know, but I'm really keen to find a standard and reliable approach to error handling that never exposes any ugly ASP.NET messages.

ASP.NET MVC customError page doesn't get displayed for some of the 400 errors

I'm having quite an interesting issue with the custom error pages management for a new ASP.NET MVC application.
This issue is like this:
- if I'm calling an URL (doesn't matter which) with a "bad" argument at the end of URL, like ..../c<, the application is displaying the correct server error page as instructed in the web.config;
- if I'm changing the URL to a more nasty one, like .../<c (to look more like an HTML tag, there is no more server error page displayed in the browser and instead of that, I'm getting a plain YSOD with a message like An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.
According to ELMAH, both requests ended with a 400 status code and the message being:
- for the first one: System.Web.HttpException (0x80004005): A potentially dangerous Request.Path value was detected from the client (<).
at System.Web.HttpRequest.ValidateInputIfRequiredByConfig()
at System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
- for the second one: System.Web.HttpException (0x80004005): A potentially dangerous Request.Path value was detected from the client (<).
at System.Web.HttpRequest.ValidateInputIfRequiredByConfig()
at System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
So, both errors are the same, the status code is the same, but for one of the errors, the custom error page is not getting displayed. I've also went to global.asax in debug mode and checked the Server.GetLastError() in protected void Application_Error(object sender, EventArgs e) and again, both errors were the same, nothing is different.
In web.config, this is how my <customErrors> tag looks like:
<customErrors mode="On" defaultRedirect="/ServerError.aspx" redirectMode="ResponseRewrite">
<error statusCode="500" redirect="/ServerError.aspx" />
<error statusCode="404" redirect="/PageNotFound.aspx" />
</customErrors>
Can please someone tell me why the behavior is different in these two cases?
Thank you very much for your time.
There's a lot of misinformation and/or out-of-date solutions regarding error handling in IIS 7+. The main things to understand are:-
.NET 4.0 made breaking changes to ASP.NET's request validation.
IIS 7+ introduced a new way of handling custom error pages.
Most people are using hodge-podge solutions involving all of customErrors, httpErrors the Application_Error handler, and often setting requestValidationMode="2.0" on the httpRuntime property and/or disabling request validation entirely! This makes it really difficult to use other people's solutions because any and all of these can affect the behaviour. I had a quick search around and I found several semi-duplicates without accepted answers, probably for this reason.
The reason that these two errors give you different behaviour is that they occur at different stages in the request pipeline. The customErrors node in your web.config interacts with errors "inside" your application, while request validation takes place "outside" your application. IIS rejects the dangerous request before it gets to your application code, and so your customErrors replacement doesn't happen.
So how do we fix that?
Ideally you want a solution with as few moving parts as possible. IIS7 gives us a new way to specify error page replacement at the IIS level instead of at the application level - the httpErrors node. This lets us catch all our errors in one place:-
<configuration>
...
<system.webServer>
...
<httpErrors errorMode="Custom" existingResponse="Replace">
<clear />
<error statusCode="400" responseMode="ExecuteURL" path="/ServerError.aspx"/>
<error statusCode="403" responseMode="ExecuteURL" path="/ServerError.aspx" />
<error statusCode="404" responseMode="ExecuteURL" path="/PageNotFound.aspx" />
<error statusCode="500" responseMode="ExecuteURL" path="/ServerError.aspx" />
</httpErrors>
...
</system.webServer>
...
</configuration>
If you care about SEO (and you should!), you still have to make sure that your controller/page sets an appropriate status code:-
this.Response.StatusCode = 500; // etc.
You should remove your customErrors node entirely. It is normally used for backwards-compatibility. You should also ensure that requestValidationMode is not set on the httpRuntime node.
This should catch most errors (excluding, obviously, errors in parsing your web.config!)
Related:- ASP.NET MVC Custom Errors
MSDN Documentation:- http://www.iis.net/configreference/system.webserver/httperrors
Note: in your case, if you want to set defaultPath on the httpErrors node, you'll get a lock violation because of ApplicationHost.config settings. You can either do as I did and just set path individually for the error codes you care about, or you can have a look at unlocking the node:-
http://www.iis.net/learn/get-started/planning-your-iis-architecture/introduction-to-applicationhostconfig
http://forums.iis.net/t/1159721.aspx
My intuition is that it's more trouble than it's worth in low-control environments like Azure App Service / Azure Web Sites. You might as well set the paths for individual status codes.
I've put a full working example of using httpErrors for custom error pages up on github. You can also see it live on azure web sites.

Error with dot (.) character in URL

I having a issue when i click to edit a user with this url in a ASP.NET MVC 3 project:
http://domain.com:8089/User/EditUser/username.surname?IDUser=e11a621p-df11-4687-9903-8bfc33c922cf
If i get another user without the '.' character, it works fine.
The error:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
I tried some tips that i find here, like:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
and:
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true" />
and this attribute on the edituser action:
[ValidateInput(false)]
But nothing seems to work. This site is hosted on a IIS server, when it was on Windows Azure WebSite, it was working as expected.
Thanks.
If you know for a fact that the edit page is the only page where you use the firstname.lastname url part, you can use the method described in this SO answer:
Prevent static file handler from intercepting filename-like URL
Specifically, in your case, adding the following web.config section should route the request to MVC:
<system.webServer>
...
<handlers>
...
<add
name="userEditPage"
path="User/EditUser/*"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
This will not be sufficient if you use the firstname.lastname in urls outside of the User/EditUser/... path, and is not a general solution. That would be much more complicated because you would need to tell IIS something like the following:
1) if the file exists, serve it (so that your .js files still serve properly)
2) Before any of the other handlers execute for the file extension, run the MVC handler and see if there is a route matching the url. Because what if you have a user of last name html?
3) If the MVC handler does not match any routes for the url, let the other handlers. Because what if you also had an .aspx page in your project?
Lastly, for the general case, you may want to consider the edge case of someone malicious creating a user with first name ../../web and lastname config? Just a thought, but it seems like the best you can hope for is restricting the use of the . in the url to specific paths.
After some headache, i publish it to Azure WebSites again and it works normally, with same web.config file that i was using in local enviroment. So the solution must be on the IIS, then after no more tries, i change the Application Pool to Default App Pool and guess what, it worked.

Display custom error page when file upload exceeds allowed size in ASP.NET MVC

My main issue is that I want to display an custom error page when an uploaded file exceeds allowed size (maxRequestLength in web.config).
When the big file is uploaded an HttpException is thrown before my upload action method in the controller is invoked. This is expected.
I have tried to catch the exception in a custom attribute and also to override OnException in the controller. Why isnt it possible to catch the exception in either the attribute or the OnException method?
Its possible though to catch the exception in Application_Error in global.asax but neither Response.Redirect nor Server.Transfer works for redirecting to the custom error page.
Server.Transfer gives the "failed to process child request" error and response.redirect gives the "Http headers already sent" error.
Any ideas?
Thanks in advance!
Marcus
When running under IIS7 and upwards there is another parameter:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="10485760" />
</requestFiltering>
</security>
</system.webServer>
The default setting is slightly less than 30 MB.
For uploaded files with size between maxRequestLength and maxAllowedContentLength IIS7 will throw an HttpException with HTTP code 500 and message text Maximum request length exceeded. When this exception is thrown, IIS7 kills the connection immediately. So an HttpModule that redirects on this error will only work if the HttpException is handled and cleared (using Server.ClearError()) in Application_Error() in global.asax.cs.
For uploaded files with size bigger than maxAllowedContentLength IIS7 will display a detailed error page with error code 404 and subStatusCode 13. The error page can be found in C:\inetpub\custerr\en-US\404-13.htm
For redirects on this error on IIS7 I recommend redirecting on httpErrors instead.
To redirect to a different action set a smaller value for maxAllowedContentLength than maxRequestLength in web.config and also add the following to web.config:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="13" />
<error statusCode="404" subStatusCode="13" prefixLanguageFilePath=""
path="http://yoursite.com/Error/UploadTooLarge" responseMode="Redirect" />
</httpErrors>
</system.webServer>
When running on IIS6, I solved it with a HttpModule by handling the BeginRequest and check if httpApplication.Context.Request.Length is larger than maxRequestLength.
To be able to redirect the entire request has to be read before redirecting.
See code example at this link:
http://www.velocityreviews.com/forums/t97027-how-to-handle-maximum-request-length-exceeded-exception.html
The velocity eviews link was really helpful in solving the issue. As stated, the only drawback was the entire request (and file) needs to be read before the redirection can be done.
But it can be limited to run only when the page where the file upload control is present by being loaded like this
if (HttpContext.Current.Request.Url.ToString().Contains("UploadedPage.aspx")
{
//read and process page request
}
You need to make a custom HttpHandler that will do this for you. ASP.NET will automatically kill the connection if the upload size is too large (as you found out).

Categories