ASP.MVC HandleError attribute not working

I know this is a common problem, but I was looking for a lot of discussion without any results.

I am trying to handle errors using HandleError ASP.MVC attrbiute. I am using MVC 4.

My error page is places in Views / Shared / Error.cshtml and looks like this:

Test error page <hgroup class="title"> <h1 class="error">Error.</h1> <h2 class="error">An error occurred while processing your request.</h2> </hgroup> 

My FilterConfig.cs in the App-Start folder:

 public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } } 

My controller:

 public class TestController : Controller { [HandleError(View = "Error")] public ActionResult Index() { throw new Exception("oops"); } } 

And finally, my Web.config file has the following node:

 <customErrors mode="On" defaultRedirect="Error"> </customErrors> 

When I call the controller action, I get a white screen with the following text:

Server error in application "/".

Runtime Error. Description: Excluded while processing your request. In addition, at run time, the user page errors for the first exception. The request was terminated.

If defaultRedirect = "Error" is not set in Web.config, then I get a yellow screen with the following text:

Server error in application "/".

Runtime Error Description: Application error on the server. The current user error settings for this application prevent application error details from being viewed.

Details To include the details of this particular error message for viewing on the local server machine, please create a tag in the configuration file "web.config" located in the root directory of the current web application. This tag must have the "mode" attribute for "RemoteOnly". To enable details that can be viewed on remote computers, set the "mode" to "Off".

Notes. The current error page that you see can be replaced with a custom error page by changing the defaultRedirect attribute of the application configuration tag to point to the user URL of the error page.

Does anyone know what could be wrong?

EDIT:

Errors were caused by using a strongly typed layout. When an error occurs, the MVC error handling engine creates a HandleErrorInfo object, which is passed to the Error view. However, if we use a strongly typed layout, the types do not match.

The solution in my case uses the Application_Error method in Global.asax, which was perfectly described below in SBirthare.

+5
source share
5 answers

Over the years, I have tried to implement "custom error handling" in ASP.NET MVC smoothly.

I successfully used Elmah before I was overwhelmed with numerous cases that needed to be handled and tested differently (i.e. local and IIS).

Recently, in one of my projects that is currently living, I used the following approach (it seems to work well in a local and production environment).

I do not specify customErrors or any settings in web.config at all.

I override Application_Error and handle all cases there, invoking specific actions in the ErrorController .

I share this if it helps, and also get feedback (although everything works, you never know when it will start to break;))

Global.asax.cs

 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); } protected void Application_Error(object sender, EventArgs e) { System.Diagnostics.Trace.WriteLine("Enter - Application_Error"); var httpContext = ((MvcApplication)sender).Context; var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); var currentController = " "; var currentAction = " "; 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(); if (ex != null) { System.Diagnostics.Trace.WriteLine(ex.Message); if (ex.InnerException != null) { System.Diagnostics.Trace.WriteLine(ex.InnerException); System.Diagnostics.Trace.WriteLine(ex.InnerException.Message); } } var controller = new ErrorController(); var routeData = new RouteData(); var action = "CustomError"; var statusCode = 500; if (ex is HttpException) { var httpEx = ex as HttpException; statusCode = httpEx.GetHttpCode(); switch (httpEx.GetHttpCode()) { case 400: action = "BadRequest"; break; case 401: action = "Unauthorized"; break; case 403: action = "Forbidden"; break; case 404: action = "PageNotFound"; break; case 500: action = "CustomError"; break; default: action = "CustomError"; break; } } else if (ex is AuthenticationException) { action = "Forbidden"; statusCode = 403; } httpContext.ClearError(); httpContext.Response.Clear(); httpContext.Response.StatusCode = statusCode; 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)); } } 

ErrorController.cs

 public class ErrorController : Controller { public ActionResult PageNotFound() { Response.StatusCode = (int)HttpStatusCode.NotFound; return View(); } public ActionResult CustomError() { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return View(); } } 

That is all I have. No HandleErrorAttribute .

I found this approach less confusing and easy to distribute. Hope this helps someone.

+15
source

Setting customErrors to on should be sufficient to display the results locally.

 <customErrors mode="On" /> 

When you register HandleErrorAttribute globally, you do not need to decorate your action method, as it will be used by default.

 public class TestController : Controller { public ActionResult Index() { throw new Exception("oops"); return View(); } } 

So far you have registered HandleErrorAttribute in filterConfig and what

 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 

is in Application_Start() in Global.asax.cs , then it should work.

If you are planning to create your own error pages, I recommend reading this blog post.

Custom Mvc Error Pages

+2
source

I use DI in almost all of my applications. Even if you do not use dependency injection, it is very useful for the global exception handler for MVC applications (Web API).

I like @SBirthare's approach, but I would put it in a class that would allow any IoC.

I prefer Autofac, but combining @SBirthare technology with some DIs should give you a centralized place to configure exception handling, but also the ability to log different types of exception handling (if you need it).

This is what I traditionally do:

 public abstract class ExceptionHandlerService : IExceptionHandlerService { ILoggingService _loggingSerivce; protected ExceptionHandlerService(ILoggingService loggingService) { //Doing this allows my IoC component to resolve whatever I have //configured to log "stuff" _loggingService = loggingService; } public virtual void HandleException(Exception exception) { //I use elmah a alot - and this can handle WebAPI //or Task.Factory ()=> things where the context is null if (Elmah.ErrorSignal.FromCurrentContext() != null) { Elmah.ErrorSignal.FromCurrentContext().Raise(exception); } else { ErrorLog.GetDefault(null).Log(new Error(exception)); } _loggingService.Log("something happened", exception) } } 

Now you need to register this

 builder.RegisterType<ExceptionHandlerService>().As<IExceptionHandlerService(); 

In an MVC application, you need to implement a class that implements IExceptionFilter

 public class CustomHandleError : IExceptionFilter { private readonly IExceptionHandlerService _exceptionHandlerService; public CustomHandleError(IExceptionHandlerService exceptionHandlerService) { _exceptionHandlerService = exceptionHandlerService; } public void OnException(ExceptionContext filterContext) { _exceptionHandlerService.HandleException(filterContext.Exception); } } 

To register filters with Autofac

 builder.Register(ctx => new CustomHandleError(ctx.Resolve<IExceptionHandlerService>())).AsExceptionFilterFor<BaseController>(); 

I always define the BaseController from which all my other controllers are. You can define an authorization filter using the same method. Now all controllers are protected and exception handled.

Now you do not need attributes for any classes - the code is in one place.

I don't have a single catch attempt, so we can save the stack trace by the time the exception handler catches the exception.

If you combine this method with @SBirthare -

 public abstract class ExceptionHandlerService : IExceptionHandlerService { ILoggingService _loggingSerivce; protected ExceptionHandlerService(ILoggingService loggingService) { //Doing this allows my IoC component to resolve whatever I have //configured to log "stuff" _loggingService = loggingService; } public virtual void HandleException(Exception exception) { //I use elmah a alot - and this can handle WebAPI //or Task.Factory ()=> things where the context is null if (Elmah.ErrorSignal.FromCurrentContext() != null) { Elmah.ErrorSignal.FromCurrentContext().Raise(exception); } else { ErrorLog.GetDefault(null).Log(new Error(exception)); } _loggingService.Log("something happened", exception) //re-direct appropriately var controller = new ErrorController(); var routeData = new RouteData(); var action = "CustomError"; var statusCode = 500; statusCode = exception.GetHttpCode(); switch (exception.GetHttpCode()) { case 400: action = "BadRequest"; break; case 401: action = "Unauthorized"; break; case 403: action = "Forbidden"; break; case 404: action = "PageNotFound"; break; case 500: action = "CustomError"; break; default: action = "CustomError"; break; } //I didn't add the Authentication Error because that should be a separate filter that Autofac resolves. var httpContext = ((MvcApplication)sender).Context; httpContext.ClearError(); httpContext.Response.Clear(); httpContext.Response.StatusCode = statusCode; 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)); } 

}

This gives the same thing, but now you are using dependency injection, and you have the ability to register multiple ExceptionHandlers and allow services based on the type of exception.

+2
source

I can't figure out what HandleErrorAttribute does. He seems to be doing nothing.

Anyway, only 4 lines of code in OnException () to force it to behave as I expected:

  // Copyright(c) 2016 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. using System.Web.Mvc; namespace GoogleCloudSamples { internal class CustomHandleErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { // Why oh Why doesn't base.OnException(filterContext) do this? ViewDataDictionary viewData = new ViewDataDictionary(filterContext); filterContext.Result = new ViewResult() { ViewName = "Error", ViewData = viewData }; filterContext.HttpContext.Response.StatusCode = 500; filterContext.ExceptionHandled = true; } } public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new CustomHandleErrorAttribute()); } } } 
0
source

DECISION:

First remove the defaultRedirect attribute in the web.config file.

Secondly, in the FilterConfig.cs file, I found that some people refer to the "custom class" version of "HandleErrorAttribute" found in some MVC templates that some developers have created on the Internet for others. They disable the ORIGINAL MVC class HandlerErrorAttribute from the default error view page.

You can fix this by making sure that the link to the ORIGINAL Microsoft MVC HandleErrorAttribute in the FilterConfig file with the โ€œusingโ€ statement looks like this by adding the global โ€œErrorโ€ View to make sure the page is now called again. See below....

 using System.Web.Mvc;//add this to make sure you are referencing the MVC version public class FilterConfig { public static void Configure(System.Web.Mvc.GlobalFilterCollection filters) { // Note I added {View = "Error"}. This applies the Error View Page to all Actions in all Controller classes filters.Add(new HandleErrorAttribute { View = "Error" }); } } 

This globally assigns the view "Error.cshtml" in the view folder to each exception thrown, except, of course, 404 and other server errors. Those that you can handle in the other ways described above. But this should direct your .NET exceptions to your custom error page. - Stokely

0
source

Source: https://habr.com/ru/post/1204634/


All Articles