@ViewBag.Message
如果给出路线:
{FeedName}/{ItemPermalink}
例如:/ Blog/Hello-World
如果该项不存在,我想返回404.在ASP.NET MVC中执行此操作的正确方法是什么?
从臀部拍摄(牛仔编码;-)),我建议这样的事情:
控制器:
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 { ///An implementation of public class HttpNotFoundResult : ActionResult { ///that throws an . Initializes a new instance of /// public HttpNotFoundResult(String message) { this.Message = message; } ///with the specified . Initializes a new instance of public HttpNotFoundResult() : this(String.Empty) { } ///with an empty message. Gets or sets the message that will be passed to the thrown public String Message { get; set; } ///. Overrides the base public override void ExecuteResult(ControllerContext context) { throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message); } } } // By Erik van Brakel, with edits from Daniel Schaffer :)functionality to throw an .
使用此方法,您遵守框架标准.那里已经有一个HttpUnauthorizedResult,所以这只会在另一个开发人员眼中扩展框架,以便稍后维护你的代码(你知道,那个知道你住在哪里的心理学家).
您可以使用反射器来查看程序集以查看HttpUnauthorizedResult是如何实现的,因为我不知道这种方法是否遗漏了任何东西(它似乎过于简单).
我没有使用反射来看一看在HttpUnauthorizedResult刚才.似乎他们设置的响应0x191(401)中的StatusCode.虽然这适用于401,使用404作为新的价值,我似乎在Firefox中越来越只是一个空白页.Internet Explorer显示默认404(不是ASP.NET版本).使用webdeveloper工具栏我检查在FF头,其确实表现出404未找到响应.可能只是我在FF中配置错误的东西.
话虽如此,我认为Jeff的方法是KISS的一个很好的例子.如果你真的不需要这个样本中的详细程度,他的方法也可以正常工作.
我们这样做; 这段代码可以在BaseController
////// returns our standard page not found view /// protected ViewResult PageNotFound() { Response.StatusCode = 404; return View("PageNotFound"); }
这样叫
public ActionResult ShowUserDetails(int? id) { // make sure we have a valid ID if (!id.HasValue) return PageNotFound();
throw new HttpException(404, "Are you sure you're in the right place?");
HttpNotFoundResult是我正在使用的第一步.返回HttpNotFoundResult很好.然后问题是,下一步是什么?
我创建了一个名为HandleNotFoundAttribute的动作过滤器,然后显示404错误页面.由于它返回一个视图,您可以为每个控制器创建一个特殊的404视图,或者让我们使用默认的共享404视图.当控制器没有指定的操作时,甚至会调用此方法,因为框架会抛出状态代码为404的HttpException.
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 }; } } }
请注意,从MVC3开始,您可以使用HttpStatusCodeResult
.
使用ActionFilter是很难维持的,因为每当我们抛出一个错误的过滤器需要在属性进行设置.如果我们忘了设置怎么办?一种方法是OnException
在基础控制器上派生.您需要定义BaseController
派生自,Controller
并且所有控制器必须派生自BaseController
.最佳实践是拥有一个基本控制器.
请注意,如果使用Exception
响应状态代码为500,那么我们需要将其更改为404 for Not Found和401 for Unauthorized.就像我上面提到的那样,使用OnException
覆盖BaseController
来避免使用filter属性.
通过向浏览器返回一个空视图,新的MVC 3也会变得更加麻烦.一些研究后的最佳解决方案是基于我在这里的答案如何在ASP.Net MVC 3中返回HttpNotFound()的视图?
为了更方便,我将它粘贴在这里:
经过一番研究.对于解决办法MVC 3这里是推导出所有的HttpNotFoundResult
,HttpUnauthorizedResult
,HttpStatusCodeResult
类和实现新的(重写它)HttpNotFound
在()方法BaseController
.
最佳做法是使用基本控制器,以便"控制"所有派生控制器.
我创建了一个新HttpStatusCodeResult
类,不是通过指定属性来派生,ActionResult
而是通过ViewResult
渲染视图或任何View
你想要的ViewName
.我按照原始HttpStatusCodeResult
设置HttpContext.Response.StatusCode
,HttpContext.Response.StatusDescription
然后base.ExecuteResult(context)
将呈现合适的视图,因为我再次得出ViewResult
.这很简单吗?希望这将在MVC核心中实现.
看到我的BaseController
吼叫:
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); } } } }
要像你这样在你的行动中使用:
public ActionResult Index() { // Some processing if (...) return HttpNotFound(); // Other processing }
并在_Layout.cshtml(如母版页)
@if (ViewBag.Message != null) {} @RenderBody()@ViewBag.Message
此外,您可以像我在代码中注释的那样使用自定义视图Error.shtml
或创建新视图NotFound.cshtml
,您可以为状态描述和其他解释定义视图模型.