Convert an exception into HTTP 404 response in the Application_Error - asp.net

First of all, quickly what exactly I want to achieve: translate particular exception into the HTTP 404 so the ASP.NET can handle it further.
I am handling exceptions in the ASP.NET (MVC2) this way:
protected void Application_Error(object sender, EventArgs e) {
var err = Server.GetLastError();
if (err == null)
return;
err = err.GetBaseException();
var noObject = err as ObjectNotFoundException;
if (noObject != null)
HandleObjectNotFound();
var handled = noObject != null;
if (!handled)
Logger.Fatal("Unhandled exception has occured in application.", err);
}
private void HandleObjectNotFound() {
Server.ClearError();
Response.Clear();
// new HttpExcepton(404, "Not Found"); // Throw or not to throw?
Response.StatusCode = 404;
Response.StatusDescription = "Not Found";
Response.StatusDescription = "Not Found";
Response.Write("The whole HTML body explaining whata 404 is??");
}
The problem is that I cannot configure default customErrors to work with it. When it is on then it never redirects to the page specified in customErrors: <error statusCode="404" redirect="404.html"/>.
I also tried to raise new HttpExcepton(404, "Not Found") from the handler but then the response code is 200 which I don't understand why.
So the questions are:
What is the proper way of translating AnException into HTTP 404 response?
How does customErrors section work when handling exceptions in Application_Error?
Why throwing HttpException(404) renders (blank) page with success (200) status?
Thanks,
Dmitriy.

In a few words, you if you manually set HTTP status in Application_Error, you lose possibility to use customErrors section handler, since you call Server.ClearError().
Handle the exception before Application_Error or derive the exception from HttpException.
What is the proper way of translating AnException into HTTP 404 response?
It's better to handle exceptions in Controller. You can introduce base class controller and handle most of exceptions in custom HandleError attribute. You can throw HttpException their and it will be properly handled by customErrors section handler.
You can also derive your ObjectNotFoundException exception from HttpException(404)
Application_Error is the last chance to handle an exception. You have only Response API to handle it. You can manually set status code and write to response or manually trigger redirect to custom error page or call Server.TransferRequest() to existing html or aspx file (not to the controller action). In current asp.net version, you cannot set or change Server.GetLastError in Application_Error method only retrieve or clear it.
How does customErrors section work when handling exceptions in Application_Error?
By calling Server.ClearError() you also clean current request error therefore it is not handled by customErrors section handler
Why throwing HttpException(404) renders (blank) page with success (200) status?
You should no throw any exception in Application_Error method. Exception means that your error handling failed.

Related

Server.ClearError() with page refresh

I am using ASP.NET custom error page in my application. Following is the web.config file entry for CustomErrors tag
<customErrors mode="On" defaultRedirect="Error.aspx" redirectMode="ResponseRewrite" />
Following is the code snippet on Error.aspx page
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
Exception ex = Server.GetLastError();
if(ex != null)
{
CommonUtils.SendException(ex.Message.ToString(), ex.StackTrace.ToString());
Server.ClearError();
}
}
}
If "ex" is not null, code will send the exception email. This works fine.
After sending email, I want to clear all the errors so that no email will be sent in case users hits the refresh button. But even after using Server.ClearError, there is a value return by Server.GetLastError() When page is posted back.
Your code should be rewritten to something like this:
Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
Exception ex= Server.GetLastError();
CommonUtils.SendException(ex.Message.ToString(), ex.StackTrace.ToString());
}
And the Page_Load event of error page should be empty then. As you can read in this MS article: https://support.microsoft.com/en-us/kb/306355 Server.ClearErrors stops error propagation, so If you call it on the page error will not be handled by Application_Error event. If you don't call it in Application_Error then application will look for customErrors declaration in web.config redirect to that page.
If you do not call Server.ClearError or trap the error in the
Page_Error or Application_Error event handler, the error is handled
based on the settings in the section of the Web.config
file. In the section, you can specify a redirect page
as a default error page (defaultRedirect) or specify to a particular
page based on the HTTP error code that is raised. You can use this
method to customize the error message that the user receives.
If an error occurs that is not trapped at any of the previous levels
in your application, this custom page is displayed. This section
demonstrates how to modify the Global.asax file so that
Server.ClearError is never called. As a result, the error is handled
in the Web.config file as the last point to trap the error.

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..

Possible to throw a 404 error within an ASP.Net page?

I was wondering if it's possible to throw a 404 error from within a page(code behind)? Or possibly even throw any other type of error page such as 408(timeout) or 401(authentication required)?
Note: I don't just want to have the page return a status code of 404, I want it to use the ASP.Net(or my CustomErrors) 404 error page.
Something like this in the code behind:
if(id>10){ //if id is greater than 10, then it doesn't exist here
throw 404Error();
}
You could throw an HttpException and set the corresponding status code:
throw new HttpException(404, "Not found");
It will also work with other status codes. Just a remark about the 401: as you probably know when ASP.NET MVC detects this code it automatically redirects you to the login page and getting a custom error page for the 401 status code could be a real PITA to implement.
A much better way is:
// Throws a 404 Not found
Response.Clear();
Response.StatusCode = 404;
Response.End();
There is no need to throw an exception and the above works much better when using custom error pages in the Web.Config
In regards of Page Load in ASP.NET WebForms needs some small workaround.
protected void Page_Load(object sender, EventArgs e)
{
HttpNotFound();
}
private void HttpNotFound()
{
Response.Clear();
Response.StatusCode = 404;
Response.End();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
If your client is capable of hitting the PHP page so code is executed, it means that you didn't get a 404 (client side error, "path/file not found"), nor did you get any other sort of 400-class error, BUT you are instead actually getting what should be classified as a 500 error of some sort.
The HTTP spec uses file/path as synonyms. This comes from the old days of the internet when it was super common for webservers to expose directory browsing lists.

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.

How do i create a custom 404 page and send a 404 msg?

I know how to redirect pages with Rewrite. But when there is a 404 page how would i display my own custom 404 page (i know i can rewrite it) and say this is in fact a 404 and not just a plain rewritten page?
-edit-
How i can SET the error code instead of asking the server? if the user goes to /user/invaliduser/ i would like to send a 403 or 404 but my rewrite would send it to a valid page to display user info so i would like to know how to say doesnt exist rather then empty fields in a page.
To be more flexible then strict 404.html follow my example.
In Global.asax.cs override:
public void Application_Error(Object sender, EventArgs e)
{
if (HttpContext.Current.Request.IsLocal) return;
Exception ex = Server.GetLastError();
HttpException httpEx = ex as HttpException;
string errorUrl;
if (httpEx != null && httpEx.GetHttpCode() == 403)
errorUrl = "~/error/403.aspx";
else if (httpEx != null && httpEx.GetHttpCode() == 404)
errorUrl = "~/error/404.aspx";
else
errorUrl = "~/error/500.aspx";
Server.Transfer(errorUrl);
}
For example my 404.aspx contains form to send feedback, 403.aspx - exposes more interesting items for user, and 500 sends alert to Admin.
U can do a 404.html and redirect the 404 error in the web.config file to your own file
<error statusCode="404" redirect="YOURFILE.htm" />

Resources