ルートが与えられた場合:
{FeedName} / {ItemPermalink}
例:/ Blog / Hello-World
アイテムが存在しない場合、404を返します。ASP.NETMVCでこれを行う正しい方法は何ですか?
ルートが与えられた場合:
{FeedName} / {ItemPermalink}
例:/ Blog / Hello-World
アイテムが存在しない場合、404を返します。ASP.NETMVCでこれを行う正しい方法は何ですか?
回答:
ヒップから撃つ(カウボーイコーディング;-))、私はこのようなものをお勧めします:
コントローラ:
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で構成を誤っただけかもしれません。
そうは言っても、ジェフのアプローチは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の使用は維持が困難です。設定を忘れた場合はどうなりますか?1つの方法はOnException
、ベースコントローラでの派生です。BaseController
派生元を定義する必要がありController
、すべてのコントローラーはから派生する必要がありますBaseController
。ベースコントローラを使用することをお勧めします。
Exception
応答ステータスコードの使用が500である場合は、Not Foundの場合は404に、Unauthorizedの場合は401に変更する必要があることに注意してください。上記で述べたように、フィルター属性の使用を避けるためにOnException
オーバーライドを使用しますBaseController
。
新しいMVC 3は、空のビューをブラウザーに返すことで、さらに面倒になります。いくつかの調査の後の最善の解決策は、ASP.Net MVC 3でHttpNotFound()のビューを返す方法についての私の回答に基づいています。
より便利にするために、ここに貼り付けます。
いくつかの研究の後。ここでのMVC 3の回避策は、すべての、、クラスを派生させHttpNotFoundResult
、HttpUnauthorizedResult
でnew(オーバーライド)()メソッド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
、コードでコメントしたような新しいビューを作成したり、ステータスの説明やその他の説明のビューモデルを定義したりできます。