I need something that would work like this:
public ActionResult Ac()
{
try {
//stuff...
}
catch(MyException ex)
{
//handle
}
}
but without putting try catch in each action method
You want to annotate your classes with HandleErrorAttribute - http://msdn.microsoft.com/en-us/library/system.web.mvc.handleerrorattribute.aspx.
If the functionality of the built in handler above isn't sufficient then you can define your own class which implements IExceptionFilter - the OnException method takes an ExceptionContext object with Result and HttpContext properties you can use to control the outcome, something like:
public class MyHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
// Do some logging etc. here
if (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled)
{
ViewResult lResult = ...
filterContext.Result = lResult;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
Use Exception Filters for exception handling.
How about
[HandleError(ExceptionType = typeof(MyException ), View = "MyErrView"))]
public ActionResult Ac()
{
//stuff
}
but with a custom HandleError Attribute that handles the type of exceptions you are targeting. This SO question should give you a good start.
Related
I've got ErrorController which customly handles my website errors.
It's pretty standard:
public class ErrorController : BaseController
{
public ActionResult Error404(Exception ex)
{
return View();
}
public ActionResult Error500(Exception ex)
{
return View();
}
}
However, in case if some rendering exception occurs inside of the View code (and this might occur, as the page has Master page (master layout) and different might happen), then I am not able to catch that rendering exception.
I can really see that exception with implementing ActionFilterAttribute.OnResultExecuted:
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
// not sure what to do here
} else base.OnResultExecuted(filterContext);
}
but in that case MVC looks for ~/Shared/Error.cshtml (incl. this path) after that exception occurs, and I can't provide the Errors view rendering exception to the user -- the "Last chance exception".
Is there any way to handle that?
Here is a nice article on Exception handling in ASP.Net MVC that should help
Method 4:- Inheriting from “HandleErrorAttribute”
public class CustomHandleErrorAttribute: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
filterContext.ExceptionHandled = true;
var model = new HandleErrorInfo(filterContext.Exception, "Controller", "Action");
filterContext.Result = new ViewResult()
{
ViewName = "Error",
ViewData = new ViewDataDictionary(model)
};
}
}
And you attach that to your base controller.
I'm using the ExceptionFilterAttribute, like this:
public class HttpExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var httpEx = context.Exception as HttpException;
if (httpEx != null)
{
var message = new HttpResponseMessage((HttpStatusCode) httpEx.GetHttpCode());
context.Response = message;
context.Exception = null;
}
}
}
So, I need to go through this filter, but if it enters the 'if' condition,
I'd like it to do not enter any more filters, that's why I did the last line:
context.Exception = null;
But it's not working. How can I do that?
Thanks a lot!
Using FilterAttribute and IExceptionFilter and implementing the OnException with the ExceptionContext parameter would avoid any other filters if an exception is raised.
public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
//Write more exception processing code if required
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult { ViewName = "Error" };
}
}
}
Tested this with OnResultExecuted. Didn't fire when an exception got handled by the aforementioned implementaion.
I've got this code.
public ActionResult Index()
{
ReceiptModel model = new ReceiptModel();
try
{
model = new ReceiptModel(context);
}
catch (BussinessException bex)
{
ModelState.AddModelError("Index", bex.MessageToDisplay);
return View("Index");
}
return View(model);
}
BussinesException ir returned from database and then displayed for user. I have to put on every controller method try-catch statement, which is a bit tedious. Is there any easier way how to handle these exceptions?
P.S. All other exceptions are handled with HandleExceptionAttribute
UPDATE:
I used Floradu88 approach. So Now i have something like this.
public sealed class HandleBussinessExceptionAttribute : HandleErrorAttribute, IExceptionFilter
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.Controller.TempData["UnhandledException"] = filterContext.Exception;
filterContext.ExceptionHandled = true;
((Controller)filterContext.Controller).ModelState.AddModelError(
((BussinessException)filterContext.Exception).Code.ToString(),
((BussinessException)filterContext.Exception).MessageToDisplay
);
filterContext.Result = new ViewResult
{
ViewName = this.View,
TempData = filterContext.Controller.TempData,
ViewData = filterContext.Controller.ViewData,
};
}
}
and on Controller action i put
[HandleBussinessExceptionAttribute(Order = 2, ExceptionType = typeof(BussinessException), View = "Login")]
i also tried in exception handler:
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(filterContext.RouteData));
and then handle error in action with ModelState.IsValid but values to action comes null. So for now i use first approach. When i have a bit more time i'll try to fix second approach.
Please read the documentation on this part:
http://msdn.microsoft.com/en-us/library/gg416513%28v=vs.98%29.aspx
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
Too much content to be posted here:
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
And your controller:
public class ProductsController : ApiController
{
[NotImplExceptionFilter]
public Contact GetContact(int id)
{
throw new NotImplementedException("This method is not implemented");
}
}
According to this post, Create a custom binder and put the model in TempData:
public class AppBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
controllerContext.Controller.TempData["model"] = bindingContext.Model;
}
}
Register it in global.asax:
ModelBinders.Binders.DefaultBinder = new AppBinder();
Create a BaseController and override the OnException:
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
this.ModelState.AddModelError(string.Empty, filterContext.Exception.Message);
filterContext.Result = new ViewResult
{
ViewName = filterContext.RouteData.Values["action"].ToString(),
TempData = filterContext.Controller.TempData,
ViewData = filterContext.Controller.ViewData
};
base.OnException(filterContext);
}
All Actions int The Controllers which inherited from this base controller will show their unhandled exceptions their own view in validation summery (remember to have
#Html.ValidationSummary(true)
in page). it works for me, hope works for you too!
I'm looking for the best approach at handling errors in a controller when returning a JSON result.
Firstly, what would be the best practice if I get an unhandled exception in my controller and I'm returning JSON? I'm thinking 400 or 500 error? Client checks the status and does whatever.
I'm playing with a FilterAttribute and an IExceptionFilter but I can't get the OnException function to call. I've got the attribute applied to a controller action.
Any ideas why that might not be calling?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class AjaxHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
// This is never called !!!
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null
&& !filterContext.ExceptionHandled)
{
...
filterContext.HttpContext.Response.StatusCode = 400;
filterContext.HttpContext.Response.StatusDescription = "Error processing request";
...
}
}
}
public class MyController : Controller
{
public MyController(IMyRepository repo)
{
...
}
[AjaxHandleError]
public ActionResult GetSomeJson(int anId)
{
throw new System.Exception();
}
}
You should decorate your controller with that attribute or in your global.asax.cs you need to add
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new AjaxHandleErrorAttribute());
}
and your OnException method will be called.
public JsonResult Menu() { // Exception }
I need application not to redirect user to the 404 page, but return special JSON result like { "result":1 }.
I wonder, is there any another solution, not try-catching.
You can implement your own FilterAttribute similar to the HandleErrorAttribute.
The HandleErrorAttribute normally does a redirect when an error occurs, but you could implement a similar attribute that returns a JsonResult. Something like the following will do:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
filterContext.Result = new JsonResult
{
Data = new { result = 1 },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.ExceptionHandled = true;
}
}
And then
[CustomHandleError]
public JsonResult Menu()
{
throw new Exception();
}
I would recommend that you download the MVC source code from CodePlex and inspect the current implementation of HandleErrorAttribute. It is a lot more subtle than my crude implementation above and you may want some of its functionality.