Where EXACTLY Do I Catch 404 And 500 Errors? - c#

I know of a couple ways to catch 404 and 500 errors. For example, I can use web.config, route.config, global.asax, and a controller; however, I don't where to put the code to make it catch these errors. Let me tell you what I've done.
Route Config:
The route.config works for 404 errors, but it won't work for 500 errors (to my knowledge). Regardless, I DON'T want to use it because I've heard that it has some downsides to it. Also, it seems to be a very poor solution to this problem IMO.
web/Web Config:
I have tried every possible web config file I have: web.config (system.web and system.webServer) and Web.config (system.web and system.webServer) as well as the Web Debug File (system.web and system.webServer). ALL of these didn't catch the error (It frustrates me because no one will tell me where EXACTLY to put the code so it catches. i.e., place1 -> place2 -> place3 -> etc... Every answer gives me the code and either says Web.config or web.config - I know that but WHERE in Web.config or web.config?) I heard this way has restrictions, but they aren't relevant to me. I know it only works for IIS 7+. I think my team and I are using IIS 8, so that shouldn't be a problem for me. I prefer a method in global.asax or web.config/Web.config.
Global Asax:
I am using the two application error handler methods Application_EndRequest and Application_Error, both of which aren't working. The only error I get is a 200 error (this error is only caught by EndRequest), which isn't an error but quite the opposite. I don't know why I would be getting this when my page shows a 404 error. To my knowledge, it's because Global Asax isn't programmed to catch these errors, but web.config and/or Web.config, and as you know, I don't know how to make that work. I will accept a global.asax solution because I haven't heard anything bad about it, yet.
Controller:
I only tried one solution: protected override void HandleUnknownAction(string actionName), and the solution called for a route in route.config {*url}. Surprise, surprise, this didn't work either (as expected). I learned that this solution using the code {*url} to find 404 errors (or at least it was the solution I searched.) And as I said earlier, I don't want a route.config solution. If I am correct, protected override void HandleUnknownAction(string actionName) may work in global but not for me.
Tried Solutions:
protected override void HandleUnknownAction(string actionName)
protected void Application_EndRequest()
protected void Application_Error(object sender, EventArgs e)
protected override void OnException(ExceptionContext filterContext)
5.
<system.webServer>
<httpErrors errorMode="Custom" defaultResponseMode="File" >
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="404"
path="404.html" />
<error statusCode="500"
path="500.html" />
</httpErrors>
</system.webServer>
6.
<httpErrors>
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath=""
path="http://yoursite.com/index.asp?syserror" responseMode="Redirect" />
</httpErrors>
7.
<customErrors mode="On">
<error statusCode="404" redirect="/Custom404.html" />
<error statusCode="500" redirect="/Custom500.html" />
</customErrors>
8.
routes.MapRoute(
"Error", // Route name
"Error/{errorCode}", // URL with parameters
new { controller = "Page", action = "Error", errorCode= UrlParameter.Optional }
);
9.
if (Context.Response.StatusCode == 404)
{
Response.Clear();
var rd = new RouteData();
rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
rd.Values["controller"] = "Errors";
rd.Values["action"] = "NotFound";
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
}
10.
Exception exception = Server.GetLastError();
// Log the exception.
ILogger logger = Container.Resolve<ILogger>();
logger.Error(exception);
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
Note: Firstly, I may have adjusted some of this solutions to fit my code, Secondly, I take no credit for this solutions. I found most, if not all, of the solutions on other forum pages. Thirdly, the only solution that worked is the 8th one; however, I believe it only works for 404s. Nevertheless, I don't want to use it because I believe it is a bad solution (correct me if I'm wrong.)
Conclusion:
I am NOT asking you to solve the solution for me. I simply need two thing: one, I need to be corrected if I was misinformed (through a comment or answer); and two, I need to know WHERE to put the code and the result of the code (through either a picture or an explanation.) If you put something like the following:
Here is code that worked for me
[Insert Code Here]
[Insert more description]
I will most likely copy the code, change it, try it, and inevitably get upset if/when it fails. If you could take the time to explain how 404 errors are caught and a global.asax or web/Web Config Solution, I will greatly appreciate it. I have been struggling with this problem for a while now, and I have put in a lot of time and effort into it, only to get vague solutions with little to no explanation as to why it catches 404/500 errors or where to put it, exactly.
Edit:
Here are the error methods I want to hit. I can assure you that they are routed in my route.config using routes.MapMvcAttributeRoutes();
[Route("~/page_not_found")]
public ActionResult PageNotFound()
{
Response.StatusCode = 404;
return View();
}
[Route("~/internal_server_error")]
public ActionResult InternalServerError()
{
Response.StatusCode = 500;
return View();
}

my experience was in this direction.
In terms of user experience, 500 redirects are made so that he does not see the wrong page. but redirecting 500 does not send exception parameters to the redirected page.
To log the error with 500, it is necessary to use global.asax.

Related

Try Catch not noticeable using ASP.NET

I've created a simple webpage called mytestpage and am using the following code:
#using System
#using System.Drawing
#using System.IO
try
{
int d = 1/0;
<p>Error not caught</p>
}
catch(Exception notUsedForTesting)
{
<p>Error caught</p>
}
Instead of seeing "Error caught" text on mytestpage, my site redirects to a differing page as follows: Error.cshtml?aspxerrorpath=/mytestpage.
I would like to know how to avoid this re-direct, and handle the exception my self.
My current web.config file contains:
<customErrors mode="RemoteOnly">
<error statusCode="500" redirect="~/Error.cshtml"/>
</customErrors>
I have tried:
<customErrors mode="Off">
</customErrors>
as well, and I get the same redirect result.
Just tested it locally. It seems like the error you are getting is not a division by zero exception but rather a compile error
What happens is that your page will be compiled on the fly since you did not enable precompile. So when you navigate to the page it will trigger a compile but that 1/0 immediately error out. Your try catch logic did not even execute.
Try doing a throw new Exception(); instead of 1/0 and it should work as expected
However I would discourage you from doing complex logic directly inside View. You should try to move the try catch logic into ViewModel or at least controller level

MVC Controller Action that doesn't modify existing response

In short, I've written an error handler controller that has a "HandleError" action that handles all http errors that I care to handle.
For a few reasons (outlined in this answer), I'm catching http errors in two places. One is a global Application_EndRequest method (managed pipeline) and the other is via the <httpErrors> section in the web config (native custom errors module).
If you're wondering why I need the <httpErrors> section, it's because certain responses aren't always caught by the managed pipeline, for example the StaticFile handler catches all urls like ".html,.txt" and will not trigger MVC code.
My controller action looks close to this
public ActionResult HandleError(int statusCode = 0, Exception exception = null)
{
string responseProcessed = Request.Headers.Get("__p__");
if (responseProcessed == null)
{
Request.Headers.Add("__p__", "1");
switch (statusCode)
{
case 401:
return Unauthorized();
case 403:
return Forbidden();
case 404:
return NotFound();
case 500:
return InternalError(exception);
default:
return UnhandledError(exception, statusCode);
}
}
else
{
return null;
}
}
My web.config httpErrors section is currently
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="/Errors/HandleError?statusCode=404" responseMode="ExecuteURL"/>
</httpErrors>
Problem
The problem is, the HandleError method is called twice per 404 response because it first hits the custom errors module (specified via the web.config) and then my Application_EndRequest hook catches the 404 which also triggers my action.
Question
The question is, how can I make my HandleError action do nothing to the response instead of replacing it with a blank response (which is what happens when you return null in an action).
Note
Please note that I am aware that I can make my custom 404 error (in web.config) point to my NotFound action explicitly and just return the view each time even though it gets called twice. There is logic (logging, etc) that still needs to only run once and I think it's better design to have it all point to the HandleError method instead to keep repetitive checking logic down and to only runs the action once.
The simple answer is... you cant. All action methods return a type of ActionResult, no matter what. If you do not need an actionresult (or the default EmptyResult that gets sent when you return null) then don't use an action method and consider using a private method instead.
If you do this, you could do as you suggest with a custom 404 and then call this private HandleError method as needed from within your controller.
If this logic needs ran for every request then consider just put it in an action filter instead.
I think you can but you might be over complicating stuff. Lets look at that first :) Does this solve your error handling of static files?
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
You can read more about it here: http://www.hanselman.com/blog/BackToBasicsDynamicImageGenerationASPNETControllersRoutingIHttpHandlersAndRunAllManagedModulesForAllRequests.aspx

asp.net mvc Response.Redirect is not working in global.asax

I am trying to redirect to error page in global.asax file of asp.net mvc5 when any exception occurs in application. After executing Response.Redirect line nothing happening.
It is not at all redirecting to errorpage which is available in the path ~\View\Shared\Error.cshtml
protected void Application_Error(object sender, EventArgs e)
{
Server.ClearError();
Response.Clear();
HttpContext.Current.Response.Redirect("~\View\Shared\Error.cshtml");
//return;
}
and in webconfig,
<system.web>
<customErrors mode="On" defaultRedirect="~\View\Shared\Error.cshtml" />
</system.web>
I am not sure what's going wrong.
My error controller:
public class ErrorController : Controller
{
// GET: Error
public ActionResult Error()
{
return View("Error");
}
}
I wouldn't recommend to use Global.asax, unless you have some custom logic going on. I'd recommend using web.config. Just be aware that, since MVC uses routes instead of physical files you should use something like this in the web.config:
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/error/404" responseMode="ExecuteUrl"/>
<httpErrors>
But in case you want to call some physical file (html, for example) you should use it in this way:
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/erro404.html" responseMode="File"/>
<httpErrors>
Now, going back to the custom logic. If you really need to use Global.asax I'd recommend you to use something like this:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
// Pass exception details to the target error View.
routeData.Values.Add("error", exception);
// Clear the error on server.
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
// Call target Controller and pass the routeData.
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));
}
You're doing it wrong.
~ returns physical paths to the root of the application, but obviously that my change.
Use Server.MapPath() method to check which path is the correct one in your case. Follow the following list:
Server.MapPath(".") returns the current physical directory of the file (e.g. aspx) being executed
Server.MapPath("..") returns the parent directory
Server.MapPath("~") returns the physical path to the root of the application
Server.MapPath("/") returns the physical path to the root of the domain name (is not necessarily the same as the root of the application)
Have a look at this brilliant answer too.

Implementing a custom error in global.asax

In my global.asax file, I have the following code:
void Application_Error(object sender, EventArgs e)
{
Exception TheError = Server.GetLastError();
if (TheError is HttpException && ((HttpException)TheError).GetHttpCode() == 404)
{
Response.Redirect("~/404.aspx");
}
else
{
Response.Redirect("~/500.aspx");
}
}
When I navigate to an non-existing page, I get the generic error page. I don't want anything pertaining to custom error in the web.config because my plan is to add code to the global.asax file to log the exceptions. Is there a way to handle custom error with just the global.asax file?
EDIT:
The answer is not obvious, but this seems to explain it: http://www.asp.net/web-forms/tutorials/deployment/deploying-web-site-projects/displaying-a-custom-error-page-cs
If you scroll down a ways, you'll find this lovely tidbit:
Note: The custom error page is only displayed when a request is made
to a resource handled by the ASP.NET engine. As we discussed in the
Core Differences Between IIS and the ASP.NET Development Server
tutorial , the web server may handle certain requests itself. By
default, the IIS web server processes requests for static content like
images and HTML files without invoking the ASP.NET engine.
Consequently, if the user requests a non-existent image file they will
get back IIS's default 404 error message rather than ASP.NET's
configured error page.
I've emphasized the first line. I tested this out in the default template, and sure enough, this URL:
http://localhost:49320/fkljflkfjelk
gets you the default IIS page, whereas simply appending a .aspx makes the Application_Error kick in. So, it sounds like you need to enable customErrors/httpErrors if you want to have all errors handled.
For IIS <= 6, add to <system.web>:
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/404.aspx"/>
<error statusCode="500" redirect="/500.aspx"/>
</customErrors>
For IIS7+, add to <system.webServer>:
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/404.aspx" responseMode="ExecuteURL"/>
<remove statusCode="500"/>
<error statusCode="500" path="/500.aspx" responseMode="ExecuteURL"/>
</httpErrors>
I'll leave the original answer in case someone finds it useful.
I believe you need to clear the existing error code from the response, otherwise IIS ignores your redirect in favor of handling the error. There's also a Boolean flag, TrySkipIisCustomErrors, you can set for newer versions of IIS (7+).
So, something like this:
void Application_Error(object sender, EventArgs e)
{
Exception TheError = Server.GetLastError();
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
if (TheError is HttpException && ((HttpException)TheError).GetHttpCode() == 404)
{
Response.Redirect("~/404.aspx");
}
else
{
Response.Redirect("~/500.aspx");
}
}
Looks like IIS is looking for a page and did not find it.
Make sure:
404.aspx is created
customErrors tag from web.config is not present.

Custom HTTP error page

In asp.net, I can define a custom error page like this:
<configuration>
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="/servererrors/404.aspx" />
</customErrors>
</system.web>
</configuration>
Now my question: If I replace, say 404.aspx with AnyHTTP.aspx,
and want to get the number of the http error to generalize the page, how do I get that error numer?
Try this setting in CustomErrors (ASP.NET 3.5 SP1):
<customErrors mode="RemoteOnly" defaultRedirect="/servererrors/AnyHTTP.aspx" RedirectMode="ResponseRewrite"/>
As a different solution, you can also do this in Global.asax:
void Application_Error(object sender, EventArgs e)
{
Server.Transfer("/servererrors/AnyHTTP.aspx");
}
and on your error page, load the last error:
Exception e = Server.GetLastError();
It is important to use Server.Transfer() in the Global.asax file; using Response.Redirect will throw a 302 error and you will lose the error that you wanted to catch.
Well you might take a look at http://www.raboof.com/projects/Elmah/ before you venture to deep into doing your own thing...
I'd recommend not using the web.config method. customErrors redirects to the error page, which makes little sense. Essentially it first says "oh yes, that'll work perfectly, you just need to go here instead", and then says "oh, we didn't find that". That's really a bug (if there isn't anything here, then why did the server tell me to go here, clearly to the user code it looks like you the server code messed up; they went to the right URI and then you directed them to the wrong one).
Use Server.Transfer() from global.asax, set a default HTTPHandler, or set IIS to execute (not redirect to) your .aspx or other file with your implementation. If you want the same handler to manage each error, then you could, for example, do a Server.Transfer() from global.asax, but include a query string parameter about the type of error (whether simply an HTTP status code, or something more detailed), or pass information in the HttpContext.

Categories

Resources