Using ActionFilter is difficult to maintain , because whenever we raise an error, the filter must be set in the attribute. What if we forget to install it? One way is to OnException on the base controller. You need to define a BaseController obtained from the Controller , and all your controllers should receive from the BaseController . It is best to have a basic controller.
Please note that when using Exception response status code is 500, so we need to change it to 404 for Not Found and 401 for Unauthorized. As mentioned above, use OnException overrides on BaseController to avoid using a filter attribute.
The new MVC 3 also becomes more annoying, returning an empty view to the browser. The best solution after some research is based on my answer here. How to return a view for HttpNotFound () in ASP.Net MVC 3?
To make it more convenient, I insert it here:
After some research. The workaround for MVC 3 here is to infer all the HttpNotFoundResult , HttpUnauthorizedResult , HttpStatusCodeResult and implement the new (overriding it) HttpNotFound () method in BaseController .
Itβs best to use a basic controller so that you βcontrolβ all the derivative controllers.
I am creating a new class HttpStatusCodeResult , not the output from ActionResult , but from ViewResult , to display the view or any View that you want by specifying the ViewName property. I follow the original HttpStatusCodeResult to set HttpContext.Response.StatusCode and HttpContext.Response.StatusDescription , but then base.ExecuteResult(context) display a suitable view, because again I get from ViewResult . Simple enough? Hope this will be implemented in the MVC core.
See my BaseController below:
using System.Web; using System.Web.Mvc; namespace YourNamespace.Controllers { public class BaseController : Controller { public BaseController() { ViewBag.MetaDescription = Settings.metaDescription; ViewBag.MetaKeywords = Settings.metaKeywords; } protected new HttpNotFoundResult HttpNotFound(string statusDescription = null) { return new HttpNotFoundResult(statusDescription); } protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null) { return new HttpUnauthorizedResult(statusDescription); } protected class HttpNotFoundResult : HttpStatusCodeResult { public HttpNotFoundResult() : this(null) { } public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { } } protected class HttpUnauthorizedResult : HttpStatusCodeResult { public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { } } protected class HttpStatusCodeResult : ViewResult { public int StatusCode { get; private set; } public string StatusDescription { get; private set; } public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { } public HttpStatusCodeResult(int statusCode, string statusDescription) { this.StatusCode = statusCode; this.StatusDescription = statusDescription; } public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } context.HttpContext.Response.StatusCode = this.StatusCode; if (this.StatusDescription != null) { context.HttpContext.Response.StatusDescription = this.StatusDescription; }
To use in your action, for example:
public ActionResult Index() { // Some processing if (...) return HttpNotFound(); // Other processing }
And in _Layout.cshtml (e.g. main page)
<div class="content"> @if (ViewBag.Message != null) { <div class="inlineMsg"><p>@ViewBag.Message</p></div> } @RenderBody() </div>
Alternatively, you can use a custom view like Error.shtml or create a new NotFound.cshtml , as I commented on the code, and you can define a view model to describe the state and other explanations.