In ASP.NET how to identify/process 404 exceptions? - asp.net

I need to handle 404 exceptions differently than all other types of them. What is the best way to identify those 404 exceptions (distinguish them from other exceptions)?
The problem is that there is no a special exception class for 404 errors, I get regular System.Web.HttpException with Message = "File does not exist."
Should I just use exception's message for it or is there a better way?
Thank you.

You can try to cast the exception as an HttpException, and then use the GetHttpCode method to check whether it is a 404 or not.
For example:
Exception ex = Server.GetLastError();
HttpException httpEx = ex as HttpException;
if (httpEx != null && httpEx.GetHttpCode() == 404)
{
//do what you want for 404 errors
}

I'd suggest that you configure your application to redirect 404 errors to a specific page, such as ~/FourOhFour.aspx. In this page you can inspect the aspxerrorpath querystring parameter, which will report the page the user was attempting to visit. From here you can do all sorts of interesting things, from logging the 404, to emailing yourself a message, to trying to determine the correct URL and auto-redirecting the user to that.
To configure your web application to redirect the user to a custom page in the face of a 404, add the following markup to web.config in the <system.web> section:
<customErrors mode="On" defaultRedirect="~/GeneralError.aspx">
<error statusCode="404" redirect="~/FourOhFour.aspx" />
</customErrors>
For more information, see:
<customErrors> element documentation

You can catch the exception. You're trying to catch this in a client application, correct?
HttpWebRequest req = ( HttpWebRequest )WebRequest.Create( someURL );
try
{
HttpWebResponse resp = req.GetResponse();
}
catch( WebException webEx )
{
if( webEx.Response != null )
{
HttpWebResponse response = webEx.Response as HttpWebResponse;
switch( response.StatusCode )
{
case HttpStatusCode.NotFound:
// do something
break;

In the Web.Config file you can specifiy a seperate File for each error code.
<customErrors mode="Off" defaultRedirect="GenericErrorPage.htm">
<error statusCode="404" redirect="FileNotFound.aspx" />
</customErrors>

Related

asp.net 5 404 handling

I have read tons of articles on handling 404 errors with the new ASP.NET 5 stuff, but I'm still unsure how to set up the following scenario:
Let Web API controllers return 404 errors, perhaps with a custom message in the response body like "the specified spell id could not be found".
All other 404 errors keep the current url, return a 404 status code, and display the contents of either an MVC error view or a static html file (don't care which).
Allow IIS to handle static files rather than ASP.NET (this condition complicates things because it means we can't put a catch-all route or a catch-all middleware to keep all requests from falling through to IIS).
Here's the example that is not working:
In Startup.cs Configure method:
app.UseErrorHandler("/error");
app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseMvc(...);
In web.config:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="/error/404" responseMode="ExecuteURL" />
<remove statusCode="500"/>
<error statusCode="500" path="/error" responseMode="ExecuteURL" />
</httpErrors>
And a very basic test controller:
[Route("api/blah")]
public class BlahController : Controller
{
// GET: spell/12345
[HttpGet("[action]/{id}")]
public IActionResult Spell(int id)
{
if (id != 12345)
return new HttpNotFoundObjectResult(new { Message = "not there bro" });
return new ObjectResult(new SpellJson
{
Id = id,
Name = "test"
});
}
}
I am running in IIS Express.
Here's what happens:
1. If I browse to a non-existent url like http://localhost:49858/asdf, I see the html generated by my /error/404 view, great!
If I call /api/blah/12345 from javascript, I get my JSON data, great!
If I call /api/blah/999 from javascript, I get a 404 error but the response body is the html generated by /error/404 rather than the JSON { Message: "not there bro" }, bad!
Which part up there is making my controller that explicitly returns a HttpNotFoundObjectResult instead execute the error page? How do I set things up to allow Web API controller methods to return 404 without going to the error page, but catch all other 404 errors with the error page?

Setting subStatusCode when throwing HttpException for use with httpErrors

I am using httpErrors in my web.config and have the following:
<error statusCode="404" subStatusCode="1" responseMode="Redirect" path="http://www.somewhere-else.com" />
Doing the following in a custom action result, works fine:
Response.StatusCode = 404;
Response.SubStatusCode = 1;
But the code that determines whether the 404 redirect is necessary is a bit further down the stack so rather than returning an actionresult, I woul prefer to throw an exception and halt further execution.
For error entries in httpErrors without a subStatusCode, I can throw the appropriate HttpException and it works as expected:
throw new HttpException(400, "Bad Request");
But I need the subStatusCode. I have tried setting Response.SubStatusCode before throwing an HttpException but it gets ignored and the default 404 error page is shown.
Any way around this?
(btw, I know subStatusCode is only internal and not exposed to client)
I realize this question is a bit old, bit figured I'd post an answer that I have come across that seems to work well enough for me. Using Asp.Net MVC 5.2, I throw an HttpException using the following:
throw new HttpException((int) HttpStatusCode.Unauthorized, "Context required", 12)
Then, in my global.asax.cs error handler:
protected void Application_Error()
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
if (httpException != null)
{
Context.Response.StatusCode = httpException.GetHttpCode();
Context.Response.SubStatusCode = httpException.ErrorCode;
Context.Server.ClearError();
}
}
Somewhat hackish, but it suits my needs..

How to redirect to a custom page if maxQueryStringLength exception occured?

How can I replace the standard error page in the case the request length exceeds the one specified in maxQueryStringLength and show the user something more friendly?
Note: Although as an HttpException this falls into generic 400th error, I want to separate the QueryLength condition and show a very specific page for this specific error. So, I cannot use "customErrors" section to indicate a page, but rather need to filter this programmatically. The problem with the below is it doesn't work.
protected virtual void Application_Error(Object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (logs != null) logs.ExceptionMsg("Application exception", this, ex);
var httpex = ex as System.Web.HttpException;
if (httpex != null && httpex.ErrorCode == -2147467259)
{
Server.ClearError();
Server.TransferRequest(#"~/main/maxlengthexceeded", false);
}
}
The problem is that Server.TransferRequest does not work. Is there an alternative to how I can tell ASP.NET which page to load?
if you are able to catch the error type/number you are getting, then you can configure a different error/redirect page only for that, here an example of configuration in the web.config:
<configuration>
<system.web>
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly">
<error statusCode="500"
redirect="InternalError.htm"/>
</customErrors>
</system.web>
</configuration>
you can see the full article here: Displaying a Custom Error Page

404 page response.redirect vs server.transfer

when handling 404 errors in ASP.NET is it ok to set 404 errors to redirect to a page that sends the 404 response code to the browser or should server.transfer be used so the 404 header can be sent to the browser while the url remains the same?
customErrors statusCode="404" results in a 302 temporary redirect then a 404 (if you've set that in your 404 page's code).
Therefore, the following should do it for you in your global.asax or error HttpModule:
protected void Application_Error(Object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
if (exception is HttpUnhandledException)
{
if (exception.InnerException == null)
{
Server.Transfer(ERROR_PAGE_LOCATION, false);
return;
}
exception = exception.InnerException;
}
if (exception is HttpException)
{
if (((HttpException)exception).GetHttpCode() == 404)
{
Server.ClearError();
Server.Transfer(NOT_FOUND_PAGE_LOCATION);
return;
}
}
if (Context != null && Context.IsCustomErrorEnabled)
Server.Transfer(ERROR_PAGE_LOCATION, false);
else
Log.Error("Unhandled exception trapped in Global.asax", exception);
}
Edit: Oh, and Best way to implement a 404 in ASP.NET put me on the road to the imperative Server.ClearError();
See http://www.andornot.com/blog/post/Handling-404-errors-with-ASPNET.aspx for a post I did that covers all of this.
I would use the customerrors section of the web.config, then you can specify the page you want 404 to go to.
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="Error.aspx">
<error statusCode="404" redirect="404Error.aspx" />
</customErrors>
</system.web>
</configuration>
On the receiving page if you want to still send the 404 you can place this in the page_load event:
Response.Status = "404 Not Found";
Response.Redirect will do 302 first than 404 on redirected page.
Server.Transfer will keep the URL, so it is 404 on requested page.
I think it all comes down SEO. I suggest using Server.Transfer as it is more clear to browser/search engine that the requested URL is not found. If you use Response.Redirect requested page is 'temporarily' redirected to a not found page. That's not good... 302 is not a good idea.
My advice is to let the ASP.NET process do the work for you based on your web.config but if you really want to to this in code you should stick with Server.Transfer cause it will save you a postback.

Where do I catch and handle maxAllowedContentLength exceeded in IIS7?

I have an aspx page where I’m allowing a user to upload a file and I want to cap the max file upload size to be 10MB. IIS7, .NET 3.5. I have the following configured in my web.config file:
<location path="foo.aspx">
<system.web>
<!-- maxRequestLength: kbytes, executionTimeout:seconds -->
<httpRuntime maxRequestLength="10240" executionTimeout="120" />
<authorization>
<allow roles="customRole"/>
<!-- Deny everyone else -->
<deny users="*"/>
</authorization>
</system.web>
<system.webServer>
<security>
<requestFiltering>
<!-- maxAllowedContentLength: bytes -->
<requestLimits maxAllowedContentLength="10240000"/>
</requestFiltering>
</security>
<handlers accessPolicy="Read, Script">
<add name="foo" path="foo.aspx" verb="POST"
type="System.Web.UI.PageHandlerFactory"
preCondition="integratedMode" />
</handlers>
</system.webServer>
</location>
I have a custom error handling module that implements IHttpModule. I’ve found that when maxRequestLength is exceeded, HttpApplication.Error does indeed get raised. However when I play with maxAllowedContentLength, the HttpApplication.Error event isn’t being raised and the user gets redirected to a 404.13 page. I've attached with Visual Studio with first chance exceptions turned on nothing is being thrown.
My first thought is to check the header content length in an earlier event – are there recommendations/best practices of where I do this? PostLogRequest? EndRequest?
After looking at the ASP.NET Application Life Cycle Overview for IIS 7.0 and doing my own experimentation, I'm assuming that the request validation is done internally by IIS before any of the events are raised.
It looks like only LogRequest, PostLogRequest, EndRequest, PreSendRequestContent, and PreSendRequestHeaders are raised after the internal validation with this error.
I've decided to attach an event handler to the HttpApplication.EndRequest event in my custom error handler and check for the 404.13 status code on POST and handle as I need it to be handled, which in my case is to redirect to the calling page which will check Server.GetLastError() and display a friendly error to the end user.
private void application_EndRequest(object sender, EventArgs e)
{
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
if ((request.HttpMethod == "POST") &&
(response.StatusCode == 404 && response.SubStatusCode == 13))
{
// Clear the response header but do not clear errors and
// transfer back to requesting page to handle error
response.ClearHeaders();
HttpContext.Current.Server.Transfer(
request.AppRelativeCurrentExecutionFilePath);
}
}
I'd welcome feedback on this approach and alternatives.
The simplest method is to handle it in the OnError method of page itself.
I think this works only in .NET 4.0, since the WebEventCode property is documented as NEW in .NET 4.0.
protected override void OnError(EventArgs e)
{
Exception err = Server.GetLastError();
if (err is HttpException)
{
if ((err as HttpException).WebEventCode == 3004)
{
Context.Items["error"] = "File exceeded maximum allowed length.";
Server.Transfer( Context.Request.Url.LocalPath );
return;
}
}
base.OnError(e);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!IsPostBack)
{
string error = Context.Items["error"] as string;
if (!string.IsNullOrEmpty( error ))
showErrorMessage( error );
}
}
What I did was:
get the last error with Server.GetLastError
check that it's a "Maximum request length exceeded." error (WebEventCode == 3004).
added a value to Context.Items collections to flag the request as an error
transfer the request back to the page itself with Server.Transfer(Context.Request.Url.LocalPath)
the page's OnLoad method checks for the error flag and displays a message if present
This ensures the error is handled entirely on the requested page, and the page is capable of reporting errors.
Also note that while the browser will eventually receive a proper response, the browser may take its time uploading the entire request before it handles the server's response and displays it. This behavior is probably defined as part of the server/browser interaction in the HTTP protocol, so probably not much can be done about that.

Resources