ルートが与えられた場合:
{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();
throw new HttpException(404, "Are you sure you're in the right place?");
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
};
}
}
}
MVC3の時点では、HttpStatusCodeResult
のみを使用できることに注意してください。
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
、HttpStatusCodeResult
クラスとnew(オーバーライド)HttpNotFound
()メソッドをBaseController
に実装します。
ベースコントローラーを使用して、派生したすべてのコントローラーを「制御」できるようにすることをお勧めします。
新しいHttpStatusCodeResult
クラスを作成し、ActionResult
から派生するのではなく、ViewResult
プロパティを指定してビューまたは任意のView
をレンダリングするViewName
から派生します。元のHttpStatusCodeResult
に従って_HttpContext.Response.StatusCode
_と_HttpContext.Response.StatusDescription
_を設定しますが、ViewResult
から派生しているため、base.ExecuteResult(context)
は適切なビューをレンダリングします。簡単ですか?これが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
_を作成したり、ステータスの説明やその他の説明のビューモデルを定義したりできます。