Wenn die Route angegeben ist:
{FeedName} / {ItemPermalink}
Beispiel: / Blog / Hello-World
Wenn das Element nicht vorhanden ist, möchte ich eine 404 zurückgeben. Wie gehe ich in ASP.NET MVC richtig vor?
Wenn die Route angegeben ist:
{FeedName} / {ItemPermalink}
Beispiel: / Blog / Hello-World
Wenn das Element nicht vorhanden ist, möchte ich eine 404 zurückgeben. Wie gehe ich in ASP.NET MVC richtig vor?
Antworten:
Wenn ich aus der Hüfte schieße (Cowboy-Codierung ;-)), würde ich so etwas vorschlagen:
Regler:
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 :)
Mit diesem Ansatz halten Sie sich an die Framework-Standards. Es gibt bereits ein HttpUnauthorizedResult, sodass dies das Framework in den Augen eines anderen Entwicklers erweitern würde, der Ihren Code später verwaltet (Sie wissen, der Psycho, der weiß, wo Sie leben).
Sie könnten einen Reflektor verwenden, um einen Blick in die Baugruppe zu werfen und zu sehen, wie das HttpUnauthorizedResult erreicht wird, da ich nicht weiß, ob bei diesem Ansatz etwas fehlt (es scheint fast zu einfach).
Ich habe gerade einen Reflektor verwendet, um mir das HttpUnauthorizedResult anzusehen. Anscheinend setzen sie den StatusCode für die Antwort auf 0x191 (401). Obwohl dies für 401 funktioniert, scheint es, wenn ich 404 als neuen Wert verwende, in Firefox nur eine leere Seite zu bekommen. Internet Explorer zeigt jedoch einen Standardwert von 404 an (nicht die ASP.NET-Version). Mit der Webentwickler-Symbolleiste habe ich die Header in FF überprüft, die eine 404 Not Found-Antwort anzeigen. Könnte einfach etwas sein, das ich in FF falsch konfiguriert habe.
Trotzdem denke ich, dass Jeffs Ansatz ein gutes Beispiel für KISS ist. Wenn Sie die Ausführlichkeit in diesem Beispiel nicht wirklich benötigen, funktioniert seine Methode ebenfalls einwandfrei.
Wir machen es so; Dieser Code befindet sich inBaseController
/// <summary>
/// returns our standard page not found view
/// </summary>
protected ViewResult PageNotFound()
{
Response.StatusCode = 404;
return View("PageNotFound");
}
so genannt
public ActionResult ShowUserDetails(int? id)
{
// make sure we have a valid ID
if (!id.HasValue) return PageNotFound();
Das HttpNotFoundResult ist ein großartiger erster Schritt zu dem, was ich verwende. Die Rückgabe eines HttpNotFoundResult ist gut. Dann ist die Frage, was kommt als nächstes?
Ich habe einen Aktionsfilter namens HandleNotFoundAttribute erstellt, der dann eine 404-Fehlerseite anzeigt. Da eine Ansicht zurückgegeben wird, können Sie pro Controller eine spezielle 404-Ansicht erstellen oder eine gemeinsam genutzte Standard-404-Ansicht verwenden. Dies wird sogar aufgerufen, wenn auf einem Controller die angegebene Aktion nicht vorhanden ist, da das Framework eine HttpException mit dem Statuscode 404 auslöst.
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
};
}
}
}
Beachten Sie, dass Sie ab MVC3 nur noch verwenden können HttpStatusCodeResult
.
HttpNotFoundResult
Die Verwendung von ActionFilter ist schwierig zu warten, da bei jedem Fehler der Filter im Attribut festgelegt werden muss. Was ist, wenn wir vergessen, es einzustellen? Eine Möglichkeit besteht darin, den OnException
Basis-Controller abzuleiten . Sie müssen eine BaseController
Ableitung von definieren Controller
und alle Ihre Controller müssen von abgeleitet sein BaseController
. Es wird empfohlen, einen Basis-Controller zu haben.
Beachten Sie, dass bei Verwendung Exception
des Antwortstatuscodes 500 für Nicht gefunden in 404 und für Nicht autorisiert in 401 geändert werden muss. Verwenden Sie, wie oben erwähnt, OnException
Overrides fürBaseController
, um die zu vermeiden.
Die neue MVC 3 macht es auch schwieriger, wenn eine leere Ansicht an den Browser zurückgegeben wird. Die beste Lösung nach einigen Recherchen basiert auf meiner Antwort hier Wie kann eine Ansicht für HttpNotFound () in ASP.Net MVC 3 zurückgeben?
Um mehr Komfort zu schaffen, füge ich es hier ein:
Nach einigem Studium. Die Abhilfe für MVC 3 hier ist alles abzuleiten HttpNotFoundResult
, HttpUnauthorizedResult
, HttpStatusCodeResult
Klassen und implementieren neuen (überschreibt es) HttpNotFound
() -Methode inBaseController
.
Es wird empfohlen, den Basis-Controller zu verwenden, damit Sie die Kontrolle über alle abgeleiteten Controller haben.
Ich erstelle eine neue HttpStatusCodeResult
Klasse, nicht um sie abzuleiten, ActionResult
sondern ViewResult
um die Ansicht oder eine beliebige zu rendern, View
indem Sie die ViewName
Eigenschaft angeben . Ich folge dem Original HttpStatusCodeResult
, um das einzustellen HttpContext.Response.StatusCode
und werde HttpContext.Response.StatusDescription
dann base.ExecuteResult(context)
aber die passende Ansicht rendern, da ich wieder davon ableite ViewResult
. Einfach genug ist es? Hoffe, dass dies im MVC-Kern implementiert wird.
Siehe meinen BaseController
Balg:
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);
}
}
}
}
So verwenden Sie Ihre Aktion:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
Und in _Layout.cshtml (wie Masterseite)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
Zusätzlich können Sie eine benutzerdefinierte Ansicht wie verwenden Error.shtml
oder eine neue erstellen, NotFound.cshtml
wie ich sie im Code kommentiert habe, und Sie können ein Ansichtsmodell für die Statusbeschreibung und andere Erklärungen definieren.