Displaying an error page MVC - c#

I am catching unhandled errors using the Application_Error method within my Global.asax file, this is what I have so far:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if(httpException != null)
{
string action;
switch(httpException.GetHttpCode())
{
case 404:
action = "404";
break;
case 500:
action = "500";
break;
default:
action = "Other";
break;
}
Server.ClearError();
Response.Redirect(String.Format("~/Error/?error=" + action + "&message=" + exception.Message));
}
}
However I really don't like the idea of redirecting the user to error page, infact I would like the URL to remain the same.
For example when a page doesn't exist it shouldn't redirect to the path in the URL, instead it should remain on the same page but still display an error.
Does anyone know how to do this?

My solution redirects much like yours, but depending on how you deploy I would do this by customising the error pages themselves in IIS.
Check Out this answered question

Related

Direct to action in lieu of 404 page

I'm making a single-page-application and the way I have it set up is that using a base controller in the OnActionExecuting() method I redirect non-ajax requests to the home index action. The path will still be there and used as an indicator to tell the javascript what to do.
That works fine for something like /login where there is actually a /login page that would normally be accessible if I hadn't blocked it using the technique I mentioned.
However when I take it a step further and use /some/other/meaningful/but/bogus/url which has no route/controller but has some meaning to the javascript, I get a 404 error.
So obviously what I'd like to do, is in that 404 situation I would like to just load the home index action instead. Alternatively a replacement for my ajax blocking that redirects all paths to the home index action (unless they're valid routes called with ajax) would have the same result.
I've searched for similar answers, unfortunately the same wording is frequently used to describe questions regarding custom 404 pages, so it's a tough one to search for.
You do want a custom error page. The difference is your custom error page is a controller/action. A custom error page doesn't have to be a page like notfound.html it is just a url so you can set the url to a controller action.
The code below will redirect unhandled status codes to "/home" i.e. the home controller. Then for 404 it will go to /controller/action which could be /home/notfound.
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="/home">
<error statusCode="404" redirect="/controller/action"/>
</customErrors>
</system.web>
The non-redirect route, in your global.asax.cs add the following code. This example maybe a little heavy but essentially achieves what you want in that the custom error controller is executed but the URL is unchanged.
protected void Application_Error(object sender, EventArgs e)
{
var httpContext = ((MvcApplication)sender).Context;
var currentController = " ";
var currentAction = " ";
var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
if (currentRouteData != null)
{
if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
{
currentController = currentRouteData.Values["controller"].ToString();
}
if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
{
currentAction = currentRouteData.Values["action"].ToString();
}
}
var ex = Server.GetLastError();
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controller = (Controller)controllerFactory.CreateController(httpContext.Request.RequestContext, "Error");
var routeData = new RouteData();
var action = "Index";
if (ex is HttpException)
{
var httpEx = ex as HttpException;
switch (httpEx.GetHttpCode())
{
case 401:
action = "Unauthorized";
break;
case 403:
action = "Forbidden";
break;
case 404:
action = "NotFound";
break;
default:
break;
}
}
httpContext.ClearError();
httpContext.Response.Clear();
httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
httpContext.Response.TrySkipIisCustomErrors = true;
routeData.Values["controller"] = "Error";
routeData.Values["action"] = action;
controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}

Application_Error renders page on local host but not on production(www)

I have an Application_Error which renders an error page:
private void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// A good location for any error logging, otherwise, do it inside of the error controller.
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Index");
// Clear the error, otherwise, we will always get the default error page.
Server.ClearError();
routeData.Values.Add("id", httpException.GetHttpCode());
Response.StatusCode = httpException.GetHttpCode();
Context.Response.ContentType = "text/html";
// Call the controller with the route
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
works on my local machine seamlessly. when i push it to production, it renders the regular asp 404 page instead of the page which the controller is supposed to server.
Even on production server, when i use localhost instead of www it works.
i should add that i have no configrations in web.config for customerrors. i tried turning them off and that didnt help either.
to me it seems like it is the url that is causing this but cant figure out why/how.
Any idea how to solve the problem?
In your Application_Error method please try adding this...
Response.TrySkipIisCustomErrors = true;

Custom error page for failed windows authentication in ASP.NET MVC application

According to How to use custom Errors page in Windows Authentication (although never marked as an answer), you have to add an IIS HTTP error handler to "catch" failed windows authentications and serve up a custom error page.
However, 403 is never reached, failed WinAuth ends with 401 Unauthorized. However, if I add an IIS HTTP error handler for 401, the NTLM authentication process does not work anymore (uses 401 internally as well).
Anybody has a working solution for a custom error page (not static, I want to execute an MVC controller action!) when windows authentication failed?
The code below isn't exactly what you need, but this is how I am handling unhandled Exceptions. You could alter this to route differently based on status code or exception type. (This is from Global.asax)
protected void Application_Error(object sender, EventArgs e)
{
var ex = Server.GetLastError().GetBaseException();
Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "Global");
int status = 0;
if (ex.GetType() == typeof(HttpException))
{
var httpException = (HttpException)ex;
var code = httpException.GetHttpCode();
status = code;
}
else
{
status = 500;
}
//Create a new error based off the exception and the error status.
NameSpace.Models.ErrorModel Error = new ErrorModel(status, ex);
string innerException = "";
if (ex.InnerException != null)
{
innerException = "\n Inner Ex: " + ex.InnerException.StackTrace;
}
log.Error("Error Id: " + Error.ErrorId + " Error: " + ex.Message + ". Stack Trace: " + ex.StackTrace + innerException);
routeData.Values.Add("error", Error);
IController errorController = new NameSpace.Controllers.ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
Update: Apologies, I did not read your post completely. If you were to perform LDAP authentication within your application you could leverage the above code to catch and handle the error with a controller. Unfortunately, I cannot provide direct guidance on your issue above.

For some invalid requests, Session object becomes null

I have a hybrid (use both MVC and classic ASP pages) ASP (C#) Net Application
and need to implement common error handling for both MVC and legacy codes;
Namely, I have to detect invalid URLs and re-route the invalid request to
either home page or login page (depending whether the user is logged in or not).
I have added the error handling code inside the 'Application_Error' (See code below).
The issue is the following: loggedin user id is kept in Session object
and for some invalid URLs session object becomes null with: "session state is not available in this context"
For example:
for the following URLs, the Session object is present:
1. http://myserver:49589/test/home/index
2. http://myserver:49589/test/home/in
3. http://myserver:49589/test/ho
But for the following URL, the session object is null:
4. http://myserver:49589/te
So, the question is why session object becomes null when I misspell the folder name in the Request, and how I can solve this issue.
Routing Map is the following:
context.MapRoute(
"default",
"test/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null) // Http Exception
{
switch (httpException.GetHttpCode())
{
case 400: // Bad Request
case 404: // Page Not Found
case 500: // Internal Server Error
{
// Clear the error on server.
Server.ClearError();
ServerConfiguration scfg = ServerConfiguration.Instance;
if (ConnxtGen.App.AppUtility.GetCurrentUserID() != -1)
{
Response.RedirectToRoute("Unity_default", new { controller = "Home", action = "Index" });
}
else
{
Response.Redirect(scfg.PagePath + "/login/login.aspx", false);
}
break;
}
default:
{
break;
}
}
}
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
}
One thing you should understand is that Session variables are only available after the HttpApplication.AcquireRequestState event has happened.
In your question, the moment where you want to get some information in the session is too early in the process of the Asp.Net page lifecycle.
I found this post which will surely better explain when the session object will be available:
Asp.net What to do if current session is null?
And here is a great article that explain in deeper details the entire internal process of Asp.Net page lifecycle:
ASP.NET Application and Page Life Cycle

How do I create a global custom error page for ASP.NET MVC3?

I brand new to ASP.NET MVC3. How would I create a global custom error page for MVC3? The general idea is when an exception is thrown it would show a generic message to the user and it would log the exception to a database for developers to investigate later.
Thanks in advance for your help.
Here is what I ended up doing in global.asax.cs:
protected void Application_Error()
{
var exception = Server.GetLastError();
Log.Error("Exception", exception);
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "General";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
IController errorsController = new ErrorController();
var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
errorsController.Execute(rc);
}
In your Global.asax file implement the Application_Error method:
protected void Application_Error() {
HttpContext ctx = HttpContext.Current;
var error = ctx.Server.GetLastError();
ctx.Response.Clear();
ctx.Response.End();
}
Following up on Maess' comment:
Read this: Error Handling in asp.net mvc 3
What I've done in my project is I created a BaseController and overridden the OnException event as below,
protected override void OnException(ExceptionContext filterContext)
{
// do some logging using log4net or signal to ELMAH
filterContext.ExceptionHandled = true;
var exModel = new HandleErrorInfo(filterContext.Exception,
filterContext.RouteData.Values["controller"].ToString(),
filterContext.RouteData.Values["action"].ToString());
View("Error", exModel).ExecuteResult(ControllerContext);
}
Also I removed the HandleError action filter registered in the Global.asax.cs.
Note: You should have a view with name Error in shared folder.
Update: To extract the error information from the Error view you have to bind the Error view to the model HandleErrorInfo.
#model System.Web.Mvc.HandleErrorInfo
Then you can easily access the exception anywhere in the view as
#Model.Exception
Create a view called Error and add it to your Views\Shared folder. Then in your catch blocks, after you have logged the error, redirect the action to the error view. If you want to display a sophisticated message create an Error model and pass it to the view, or put the information into the ViewBag.
For information on unhandled exceptions and HTTP errors see Ropstah's post.

Categories

Resources