What is the correct way to send an HTTP 404 response from an ASP.NET MVC action?

If a route is specified:

{FeedName} / {ItemPermalink}

ex: / Blog / Hello-World

If the item does not exist, I want to return the value 404. What is the correct way to do this in ASP.NET MVC?

+84
Jan 31 '09 at 23:48
source share
6 answers

Shooting from the hip (cowboy coding ;-)), I would suggest something like this:

Controller:

public class HomeController : Controller { public ActionResult Index() { return new HttpNotFoundResult("This doesn't exist"); } } 



HttpNotFoundResult:

 using System; using System.Net; using System.Web; using System.Web.Mvc; namespace YourNamespaceHere { /// <summary>An implementation of <see cref="ActionResult" /> that throws an <see cref="HttpException" />.</summary> public class HttpNotFoundResult : ActionResult { /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with the specified <paramref name="message"/>.</summary> /// <param name="message"></param> public HttpNotFoundResult(String message) { this.Message = message; } /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with an empty message.</summary> public HttpNotFoundResult() : this(String.Empty) { } /// <summary>Gets or sets the message that will be passed to the thrown <see cref="HttpException" />.</summary> public String Message { get; set; } /// <summary>Overrides the base <see cref="ActionResult.ExecuteResult" /> functionality to throw an <see cref="HttpException" />.</summary> public override void ExecuteResult(ControllerContext context) { throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message); } } } // By Erik van Brakel, with edits from Daniel Schaffer :) 

Using this approach, you adhere to framework standards. There is already an HttpUnauthorizedResult there, so this will just expand the scope in the eyes of another developer supporting your code later (you know, a psychologist who knows where you live).

You can use a reflector to look into the assembly, to see how the HttpUnauthorizedResult is achieved, because I don't know if this approach missed something (it seems like it's too simple).




I used a reflector to take a look at the HttpUnauthorizedResult just now. They seem to set the StatusCode in response to 0x191 (401). Although this works for 401, using 404 as the new value, I seem to get only a blank page in Firefox. Internet Explorer shows 404 by default (and not ASP.NET version). Using the webdeveloper toolbar, I checked the headers in FF, which DO show a 404 Not Found response. Maybe just something I misconfigured in FF.




Having said that, I think Jeff's approach is a great example of KISS. If you really don't need the details in this example, his method also works just fine.

+66
Feb 01 '09 at 0:26
source

We do it like this; this code is in BaseController

 /// <summary> /// returns our standard page not found view /// </summary> protected ViewResult PageNotFound() { Response.StatusCode = 404; return View("PageNotFound"); } 

called so

 public ActionResult ShowUserDetails(int? id) { // make sure we have a valid ID if (!id.HasValue) return PageNotFound(); 
+44
Feb 01 '09 at 0:39
source
 throw new HttpException(404, "Are you sure you're in the right place?"); 
+19
Feb 01 '09 at 0:04
source

Note that with MVC3 you can just use HttpStatusCodeResult .

+7
Feb 04 2018-11-11T00:
source

HttpNotFoundResult is a great first step to what I'm using. Return HttpNotFoundResult is good. Then the question arises: what next?

I created an action filter called HandleNotFoundAttribute, which then shows the 404 error page. Since it returns a view, you can create a special view 404 for each controller or let you use the default 404 sharing. This will even be triggered if the controller does not have the specified action, because the framework throws an HttpException with a status code of 404.

 public class HandleNotFoundAttribute : ActionFilterAttribute, IExceptionFilter { public void OnException(ExceptionContext filterContext) { var httpException = filterContext.Exception.GetBaseException() as HttpException; if (httpException != null && httpException.GetHttpCode() == (int)HttpStatusCode.NotFound) { filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; // Prevents IIS from intercepting the error and displaying its own content. filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound; filterContext.Result = new ViewResult { ViewName = "404", ViewData = filterContext.Controller.ViewData, TempData = filterContext.Controller.TempData }; } } } 
+6
Jun 22 '10 at 17:42
source

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; } // 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, 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.

+5
Mar 10 2018-11-11T00:
source



All Articles