Nếu đưa ra lộ trình:
{FeedName} / {ItemPermalink}
ví dụ: / Blog / Hello-World
Nếu mục không tồn tại, tôi muốn trả về 404. Cách phù hợp để thực hiện việc này trong ASP.NET MVC là gì?
Nếu đưa ra lộ trình:
{FeedName} / {ItemPermalink}
ví dụ: / Blog / Hello-World
Nếu mục không tồn tại, tôi muốn trả về 404. Cách phù hợp để thực hiện việc này trong ASP.NET MVC là gì?
Câu trả lời:
Chụp từ hông (mã hóa cao bồi ;-)), tôi muốn đề xuất một cái gì đó như thế này:
Bộ điều khiển:
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 :)
Sử dụng cách tiếp cận này, bạn tuân thủ các tiêu chuẩn khung. Đã có HttpUnauthorizedResult ở đó, vì vậy điều này chỉ đơn giản là mở rộng khuôn khổ dưới con mắt của một nhà phát triển khác đang duy trì mã của bạn sau này (bạn biết đấy, người tâm lý biết bạn sống ở đâu).
Bạn có thể sử dụng gương phản xạ để nhìn vào tổ hợp để xem HttpUnauthorizedResult đạt được như thế nào, bởi vì tôi không biết liệu cách tiếp cận này có bỏ sót điều gì không (nó có vẻ quá đơn giản).
Tôi đã sử dụng gương phản xạ để xem HttpUnauthorizedResult vừa rồi. Có vẻ như họ đang đặt Mã trạng thái trên phản hồi với 0x191 (401). Mặc dù điều này hoạt động cho 401, sử dụng 404 làm giá trị mới, tôi dường như chỉ nhận được một trang trống trong Firefox. Internet Explorer hiển thị mặc định 404 (không phải phiên bản ASP.NET). Sử dụng thanh công cụ của trình phát triển web, tôi đã kiểm tra các tiêu đề trong FF, tiêu đề này sẽ hiển thị phản hồi 404 Not Found. Có thể chỉ đơn giản là một cái gì đó tôi đã định cấu hình sai trong FF.
Điều này đang được nói, tôi nghĩ cách tiếp cận của Jeff là một ví dụ điển hình về KISS. Nếu bạn không thực sự cần sự chi tiết trong mẫu này, phương pháp của anh ấy cũng hoạt động tốt.
Chúng tôi làm như vậy; mã này được tìm thấy trongBaseController
/// <summary>
/// returns our standard page not found view
/// </summary>
protected ViewResult PageNotFound()
{
Response.StatusCode = 404;
return View("PageNotFound");
}
được gọi như vậy
public ActionResult ShowUserDetails(int? id)
{
// make sure we have a valid ID
if (!id.HasValue) return PageNotFound();
HttpNotFoundResult là bước đầu tiên tuyệt vời cho những gì tôi đang sử dụng. Trả lại một HttpNotFoundResult là tốt. Sau đó, câu hỏi là, tiếp theo là gì?
Tôi đã tạo một bộ lọc hành động có tên HandleNotFoundAttribute sau đó hiển thị trang lỗi 404. Vì nó trả về một dạng xem, bạn có thể tạo một dạng xem 404 đặc biệt cho mỗi bộ điều khiển hoặc sử dụng dạng xem 404 được chia sẻ mặc định. Điều này thậm chí sẽ được gọi khi một bộ điều khiển không có hành động được chỉ định, bởi vì khung công tác ném một HttpException với mã trạng thái là 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
};
}
}
}
Lưu ý rằng kể từ MVC3, bạn chỉ có thể sử dụng HttpStatusCodeResult
.
HttpNotFoundResult
Sử dụng ActionFilter là khó để duy trì bởi vì bất cứ khi nào chúng ta ném ra một lỗi cần lọc được thiết lập trong các thuộc tính. Điều gì sẽ xảy ra nếu chúng ta quên thiết lập nó? Một cách là bắt nguồn OnException
từ bộ điều khiển cơ sở. Bạn cần xác định một BaseController
nguồn gốc Controller
và tất cả các bộ điều khiển của bạn phải bắt nguồn từ đó BaseController
. Cách tốt nhất là có một bộ điều khiển cơ sở.
Lưu ý nếu sử dụng Exception
mã trạng thái phản hồi là 500, vì vậy chúng ta cần thay đổi nó thành 404 cho Không tìm thấy và 401 cho Không được phép. Giống như tôi đã đề cập ở trên, hãy sử dụng OnException
ghi đè trên BaseController
để tránh sử dụng thuộc tính bộ lọc.
MVC 3 mới cũng gây ra nhiều rắc rối hơn bằng cách trả lại một chế độ xem trống cho trình duyệt. Giải pháp tốt nhất sau một số nghiên cứu dựa trên câu trả lời của tôi ở đây Làm thế nào để trả lại chế độ xem cho HttpNotFound () trong ASP.Net MVC 3?
Để thuận tiện hơn, tôi dán nó vào đây:
Sau một số nghiên cứu. Cách giải quyết cho MVC 3 ở đây là để lấy được tất cả HttpNotFoundResult
, HttpUnauthorizedResult
, HttpStatusCodeResult
lớp học và thực hiện mới (trọng nó) HttpNotFound
() phương pháp trong BaseController
.
Cách tốt nhất là sử dụng Bộ điều khiển cơ sở để bạn có 'quyền kiểm soát' đối với tất cả Bộ điều khiển có nguồn gốc.
Tôi tạo HttpStatusCodeResult
lớp mới , không phải để bắt nguồn ActionResult
mà từ đó ViewResult
để hiển thị dạng xem hoặc bất kỳ lớp nào View
bạn muốn bằng cách chỉ định thuộc ViewName
tính. Tôi làm theo bản gốc HttpStatusCodeResult
để thiết lập HttpContext.Response.StatusCode
và HttpContext.Response.StatusDescription
nhưng sau đó base.ExecuteResult(context)
sẽ hiển thị chế độ xem phù hợp bởi vì một lần nữa tôi bắt nguồn từ ViewResult
. Đủ đơn giản phải không? Hy vọng điều này sẽ được thực hiện trong lõi MVC.
Xem BaseController
dưới đây của tôi :
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);
}
}
}
}
Để sử dụng trong hành động của bạn như thế này:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
Và trong _Layout.cshtml (như trang chính)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
Ngoài ra, bạn có thể sử dụng chế độ xem tùy chỉnh giống như Error.shtml
hoặc tạo mới NotFound.cshtml
như tôi đã nhận xét trong mã và bạn có thể xác định mô hình chế độ xem cho mô tả trạng thái và các giải thích khác.