경로가 주어진 경우 :
{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를 살펴 보았습니다. 0x191 (401)에 대한 응답에 StatusCode를 설정하는 것 같습니다. 이것은 401에서 작동하지만 404를 새 값으로 사용하면 Firefox에서 빈 페이지가 나타나는 것 같습니다. Internet Explorer는 기본 404를 표시합니다 (ASP.NET 버전이 아님). 웹 개발자 도구 모음을 사용하여 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를 반환하는 것이 좋습니다. 그럼 문제는 다음은 무엇입니까?
404 오류 페이지를 표시하는 HandleNotFoundAttribute라는 작업 필터를 만들었습니다. 보기를 반환하기 때문에 컨트롤러 당 특별한 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
파생 된 을 정의해야 Controller
하며 모든 컨트롤러는 다음에서 파생되어야합니다.BaseController
. 기본 컨트롤러를 사용하는 것이 가장 좋습니다.
Exception
응답 상태 코드를 사용 하는 것이 500이면 찾을 수 없음의 경우 404로, 권한이없는 경우 401로 변경해야합니다. 위에서 언급했듯이 필터 속성을 사용하지 않으 려면 OnException
재정의를 BaseController
사용하십시오.
새로운 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 (예 : 마스터 페이지)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
또한 사용자 정의보기를 사용 Error.shtml
하거나 NotFound.cshtml
내가 코드에 주석을 추가 한 것처럼 새로 만들 수 있으며 상태 설명 및 기타 설명에 대한보기 모델을 정의 할 수 있습니다.