404 page response.redirect vs server.transfer - asp.net

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.

Related

ASP.NET MVC4 error handling for 404, 401 and other exceptions

I'm struggling to understand how to correctly handle errors in ASP.NET MVC4. As an example, I've created a new MVC4 project using the "Internet Application" template and updated my home controller to test out some error cases:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Hello";
return View();
}
public ActionResult About()
{
throw new HttpException(401, "Not Authorized");
}
public ActionResult Contact()
{
throw new Exception("Oh no, some error occurred...");
}
}
I have enabled customErrors in my web.config file:
<customErrors mode="On"></customErrors>
When I run the app and click "Contact", I see the ~/Views/Shared/Error.cshtml view as expected, since I have the HandleErrorAttribute registered as a global filter.
However, when I click "About", I get the standard ASP.NET yellow error page that says "Runtime Error". Why are these two exceptions being handled differently and how can I get instances of HttpException to get caught using the HandleError attribute?
CustomErrors config
Ideally, I'd like custom error pages for the following:
A custom 404 (not found) page that's nice and user friendly
A custom 401 (not authorised) page that informs the user that they do not have access (e.g. thrown after checking permissions for a particular item in the model)
A generic error page that is used in all other cases (in place of the standard yellow ASP.NET page).
I've created a new "Error" controller with views for each of the scenarios above. I have then updated customErrors in web.config like so:
<customErrors mode="On" defaultRedirect="~/Error/Trouble">
<error statusCode="404" redirect="~/Error/NotFound"></error>
<error statusCode="401" redirect="~/Error/NotAuthorized"></error>
</customErrors>
The 404 page works fine, but I don't get the 401 page at all. Instead, I get the ~/Error/Trouble view (the one specified as the defaultRedirect) when I try to access the About action on the Home controller.
Why is my custom 401 redirect page not working?
ASP.NET uses 401's internally to redirect users to the login page. Wherever you were planning to throw a 401 unauthorized, instead throw a 403 forbidden.
If you really need to return a 401 and not a 403, then you can use:
HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true
I had a similar problem where I could not get 401 errors to go to my page despite the change to the web.config.
For a 401 you will probably be seeing the standard 401 Unauthorised page, even if you have added 401 to the customerrors section in your web.config. I read that when using IIS and Windows Authentication the check happens before ASP.NET even sees the request, hence you see it's own 401.
For my project I edited the Global.asax file to redirect to a route I had created for 401 errors, sending the user to the "Unauthorised to see this" view.
In the Global.asax:
void Application_EndRequest(object sender, System.EventArgs e)
{
// If the user is not authorised to see this page or access this function, send them to the error page.
if (Response.StatusCode == 401)
{
Response.ClearContent();
Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults);
}
}
and in the Route.config:
routes.MapRoute(
"ErrorHandler",
"Error/{action}/{errMsg}",
new { controller = "Error", action = "Unauthorised", errMsg = UrlParameter.Optional }
);
and in the controller:
public ViewResult Unauthorised()
{
//Response.StatusCode = 401; // Do not set this or else you get a redirect loop
return View();
}

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

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

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>

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" />

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