Useful info from @Darin Dimitrov that HttpNotFoundResult
is actually returning empty result.
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)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
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.