หากระบุเส้นทาง:
{FeedName} / {ItemPermalink}
เช่น / Blog / Hello-World
หากไม่มีรายการอยู่ฉันต้องการส่งคืน 404 วิธีที่ถูกต้องในการดำเนินการนี้ใน ASP.NET MVC คืออะไร?
หากระบุเส้นทาง:
{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
{
/// <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) การใช้แถบเครื่องมือนักพัฒนาเว็บฉันตรวจสอบส่วนหัวใน FF ซึ่ง DO แสดงการตอบสนอง 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();
throw new HttpException(404, "Are you sure you're in the right place?");
web.config
.
HttpNotFoundResult เป็นขั้นตอนแรกที่ยอดเยี่ยมสำหรับสิ่งที่ฉันใช้ การส่งคืน HttpNotFoundResult เป็นสิ่งที่ดี คำถามคืออะไรต่อไป?
ฉันสร้างตัวกรองการดำเนินการที่เรียกว่า HandleNotFoundAttribute ซึ่งจะแสดงหน้าข้อผิดพลาด 404 เนื่องจากจะส่งคืนมุมมองคุณสามารถสร้างมุมมอง 404 พิเศษต่อคอนโทรลเลอร์หรือให้ใช้มุมมอง 404 ที่แชร์เริ่มต้น สิ่งนี้จะถูกเรียกเมื่อคอนโทรลเลอร์ไม่มีการดำเนินการที่ระบุไว้เนื่องจากเฟรมเวิร์กแสดง HttpException ด้วยรหัสสถานะเป็น 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
};
}
}
}
โปรดทราบว่าใน MVC3 คุณสามารถใช้HttpStatusCodeResult
ไฟล์.
HttpNotFoundResult
การใช้ActionFilterนั้นยากที่จะรักษาเพราะเมื่อใดก็ตามที่เราเกิดข้อผิดพลาดจะต้องตั้งค่าตัวกรองในแอตทริบิวต์ ถ้าเราลืมตั้งค่าล่ะ? วิธีหนึ่งคือได้มาOnException
จากตัวควบคุมฐาน คุณจำเป็นต้องกำหนดBaseController
มาจากและตัวควบคุมของคุณทุกคนจะต้องมาจากController
BaseController
เป็นแนวทางปฏิบัติที่ดีที่สุดในการมีตัวควบคุมฐาน
โปรดทราบว่าหากใช้Exception
รหัสสถานะการตอบกลับคือ 500 ดังนั้นเราจำเป็นต้องเปลี่ยนเป็น 404 สำหรับ Not Found และ 401 สำหรับ Unauthorized เช่นเดียวกับที่ฉันกล่าวถึงข้างต้นให้ใช้การOnException
แทนที่BaseController
เพื่อหลีกเลี่ยงการใช้แอตทริบิวต์ตัวกรอง
MVC 3 ใหม่ยังทำให้เกิดปัญหามากขึ้นด้วยการคืนมุมมองที่ว่างเปล่าไปยังเบราว์เซอร์ ทางออกที่ดีที่สุดหลังจากการวิจัยมาจากคำตอบของฉันที่นี่จะคืนมุมมองสำหรับ HttpNotFound () ใน ASP.Net MVC 3 ได้อย่างไร
เพื่อให้สะดวกสบายมากขึ้นฉันวางไว้ที่นี่:
หลังจากการศึกษาบางส่วน วิธีแก้ปัญหาสำหรับ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 (เช่นหน้าต้นแบบ)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
นอกจากนี้คุณสามารถใช้มุมมองแบบกำหนดเองเช่นError.shtml
หรือสร้างใหม่NotFound.cshtml
เหมือนที่ฉันแสดงความคิดเห็นในโค้ดและคุณอาจกำหนดรูปแบบมุมมองสำหรับคำอธิบายสถานะและคำอธิบายอื่น ๆ