@ViewBag.Message
If given the route:
{FeedName}/{ItemPermalink}
ex: /Blog/Hello-World
If the item doesn\'t exist, I want to return a 404. What is the
Using ActionFilter is hard to maintain because whenever we throw an error the filter need to be set in the attribute. What if we forget to set it? One way is deriving OnException on base controller. You need to define a BaseController derived from Controller and all your controllers must derive from BaseController. It is a best practise to have a base controller.
Note if using Exception the response status code is 500, so we need to change it to 404 for Not Found and 401 for Unauthorized. Just like I mention above, use OnException overrides on BaseController to avoid using filter attribute.
The new MVC 3 also make more troublesome by returning an empty view to 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 more convinience I paste it here:
After some study. The workaround for MVC 3 here is to derive all HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult classes and implement new (overriding it) HttpNotFound() method in BaseController.
It is best practise to use base Controller so you have 'control' over all derived Controllers.
I create new HttpStatusCodeResult class, not to derive from ActionResult but from ViewResult to render the view or any View you want by specifying the ViewName property. I follow the original HttpStatusCodeResult to set the HttpContext.Response.StatusCode and HttpContext.Response.StatusDescription but then base.ExecuteResult(context) will render the suitable view because again I derive from ViewResult. Simple enough is it? Hope this will be implemented in the MVC core.
See my BaseController bellow:
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;
}
// 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
// 2. Uncomment this and change to any custom view and set the name here or simply
// 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
//this.ViewName = "Error";
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
}
}
To use in your action like this:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
And in _Layout.cshtml (like master page)
@if (ViewBag.Message != null)
{
@ViewBag.Message
}
@RenderBody()
Additionally you can use a custom view like Error.shtml or create new NotFound.cshtml like I commented in the code and you may define a view model for the status description and other explanations.