如果给出路线:
{FeedName} / {ItemPermalink}
例如:/ Blog / Hello-World
如果该项目不存在,我想返回404。在ASP.NET MVC中执行此操作的正确方法是什么?
如果给出路线:
{FeedName} / {ItemPermalink}
例如:/ Blog / Hello-World
如果该项目不存在,我想返回404。在ASP.NET MVC中执行此操作的正确方法是什么?
Answers:
从臀部射击(牛仔编码;-)),我建议这样:
控制器:
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 :)
使用此方法,您符合框架标准。那里已经有了一个HttpUnauthorizedResult,因此这将在以后继续维护您代码的另一个开发人员的眼中扩展框架(您知道,知道您居住地的心理医生)。
您可以使用反射器查看程序集,以了解如何实现HttpUnauthorizedResult,因为我不知道这种方法是否会遗漏任何东西(这似乎太简单了)。
我刚才确实使用了反射器来查看HttpUnauthorizedResult。似乎他们将响应的StatusCode设置为0x191(401)。尽管这适用于401,但使用404作为新值,我似乎在Firefox中仅获得一个空白页面。Internet Explorer会显示默认的404(不是ASP.NET版本)。使用webdeveloper工具栏,我检查了FF中的标头,这些标头确实显示404 Not Found响应。可能只是我在FF中配置错误而已。
话虽这么说,我认为Jeff的方法是KISS的一个很好的例子。如果您在此示例中确实不需要冗长,那么他的方法也可以正常工作。
我们这样做是这样;该代码位于BaseController
/// <summary>
/// returns our standard page not found view
/// </summary>
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();
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
};
}
}
}
使用ActionFilter是很难维持的,因为每当我们抛出一个错误的过滤器需要的属性进行设置。如果我们忘记设置该怎么办?一种方法是OnException
基于基本控制器。您需要定义一个BaseController
from 的派生,Controller
并且您的所有控制器都必须从派生BaseController
。最好有一个基本控制器。
请注意,如果使用Exception
的响应状态代码是500,那么对于未找到,我们需要将其更改为404,对于未授权,则需要将其更改为401。就像我上面提到的,OnException
在BaseController
以避免使用filter属性。
通过向浏览器返回空视图,新的MVC 3也会带来更多麻烦。经过研究后的最佳解决方案基于我的回答如何在ASP.Net MVC 3中返回HttpNotFound()的视图?
为了更加方便起见,我将其粘贴在此处:
经过研究。对于解决办法MVC 3这里是推导出所有的HttpNotFoundResult
,HttpUnauthorizedResult
,HttpStatusCodeResult
类和实现新的(重写它)HttpNotFound
在()方法BaseController
。
最佳实践是使用基本控制器,以便您可以“控制”所有派生的控制器。
我创建新HttpStatusCodeResult
类,而不是从中派生ActionResult
而是通过指定属性ViewResult
来呈现视图或所需的任何类。我按照原图设置,然后再渲染合适的视图,因为我还是从派生的。很简单吗?希望这将在MVC核心中实现。View
ViewName
HttpStatusCodeResult
HttpContext.Response.StatusCode
HttpContext.Response.StatusDescription
base.ExecuteResult(context)
ViewResult
看到我的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中(如母版页)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
此外,您可以使用自定义视图,也可以像代码中注释的那样Error.shtml
创建新视图NotFound.cshtml
,并且可以为状态描述和其他说明定义视图模型。