私はRC2を使っています
URLルーティングを使用する:
routes.MapRoute(
"Error",
"{*url}",
new { controller = "Errors", action = "NotFound" } // 404s
);
上記は初期のMVCプロジェクトによって設定されたデフォルトのルートテーブルを仮定して、このようなリクエストを処理するようです。 "/ blah/blah/blah/blah"
コントローラ自体のHandleUnknownAction()をオーバーライドします。
// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
ViewData["actionName"] = actionName;
View("NotFound").ExecuteResult(this.ControllerContext);
}
しかしながら、以前の戦略はBad/Unknownコントローラへのリクエストを処理しません。たとえば、 "/ IDoNotExist"がない場合、これを要求するとWebサーバーから一般的な404ページが取得され、routing + overrideを使用する場合は404ページが表示されません。
それでは最後に、私の質問は次のとおりです。MVCフレームワーク自体の中で、経路などを使ってこのタイプの要求を捉える方法はありますか?
または私の404ハンドラとしてデフォルトでWeb.Config customErrorsを使用することをデフォルトにして、これをすべて忘れるべきですか? customErrorsを使用する場合は、直接アクセスに対するWeb.Configの制限により、/ Viewsの外部に一般的な404ページを格納する必要があります。
コードは http://blogs.Microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2から取られます.aspx そしてASP.net MVC 1.0でも動作します
これは私がどのようにHTTP例外を処理するかです:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// Log the exception.
ILogger logger = Container.Resolve<ILogger>();
logger.Error(exception);
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
// Pass exception details to the target error View.
routeData.Values.Add("error", exception);
// Clear the error on server.
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
// Call target Controller and pass the routeData.
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));
}
以下は404ソリューションの私の要件であり、以下私はそれを実装する方法を示します:
未処理の例外やログ記録( Shay Jacoby's answer が示すように)のようなより高いことには、Application_Error
をGlobal.asaxに保存するべきですが、404処理はしないでください。これが私の提案がGlobal.asaxファイルから404のものを締め出す理由です。
これは保守容易性のための良い考えです。 ErrorController を使用すると、将来的に よく設計された404ページ を容易に調整できるようになります。また、あなたの応答が404コードを持っていることを確認してください。
public class ErrorController : MyController
{
#region Http404
public ActionResult Http404(string url)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
var model = new NotFoundViewModel();
// If the url is relative ('NotFound' route) then replace with Requested path
model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
Request.Url.OriginalString : url;
// Dont get the user stuck in a 'retry loop' by
// allowing the Referrer to be the same as the Request
model.ReferrerUrl = Request.UrlReferrer != null &&
Request.UrlReferrer.OriginalString != model.RequestedUrl ?
Request.UrlReferrer.OriginalString : null;
// TODO: insert ILogger here
return View("NotFound", model);
}
public class NotFoundViewModel
{
public string RequestedUrl { get; set; }
public string ReferrerUrl { get; set; }
}
#endregion
}
HandleUnknownAction
を配線することができます。ASP.NET MVCの404は、さまざまな場所で捕捉される必要があります。 1つ目はHandleUnknownAction
です。
InvokeHttp404
メソッドはErrorController
と新しいHttp404
アクションへの再ルーティングのための共通の場所を作成します。と思う DRY !
public abstract class MyController : Controller
{
#region Http404 handling
protected override void HandleUnknownAction(string actionName)
{
// If controller is ErrorController dont 'nest' exceptions
if (this.GetType() != typeof(ErrorController))
this.InvokeHttp404(HttpContext);
}
public ActionResult InvokeHttp404(HttpContextBase httpContext)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error");
errorRoute.Values.Add("action", "Http404");
errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
errorController.Execute(new RequestContext(
httpContext, errorRoute));
return new EmptyResult();
}
#endregion
}
そのように(StructureMapである必要はありません):
MVC1.0の例:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
try
{
if (controllerType == null)
return base.GetControllerInstance(controllerType);
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);
return errorController;
}
else
throw ex;
}
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}
MVC2.0の例:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 404)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);
return errorController;
}
else
throw ex;
}
return ObjectFactory.GetInstance(controllerType) as Controller;
}
エラーが発生した場所により近いところでエラーを検出する方が良いと思います。これが私がApplication_Error
ハンドラよりも上記を好む理由です。
これは404をキャッチする2番目の場所です。
このルートはHttp404
アクションを指しているはずです。ルーティングエンジンがここでドメイン部分を削除しているため、url
パラメータが相対URLになることに注意してください。そのため、ステップ1ですべての条件付きURLロジックを使用できます。
routes.MapRoute("NotFound", "{*url}",
new { controller = "Error", action = "Http404" });
これはMVCアプリで404をキャッチするための3番目で最後の場所です。あなたがここで比類のないルートを見つけられない場合、MVCはASP.NET(Global.asax)に問題を渡します、そしてあなたは本当にこの状況でそれを望みません。
悪いIDが私のLoansコントローラに送信されたときのように(MyController
から派生):
//
// GET: /Detail/ID
public ActionResult Detail(int ID)
{
Loan loan = this._svc.GetLoans().WithID(ID);
if (loan == null)
return this.InvokeHttp404(HttpContext);
else
return View(loan);
}
これらすべてをより少ないコードでより少ない場所に接続することができればいいのですが、この解決策はより保守可能で、よりテスト可能で、かなり実用的だと思います。
これまでのご意見ありがとうございます。もっと手に入れたいです。
注:これは私の最初の答えから大幅に編集されていますが、目的/要件は同じです - これが私が新しい答えを追加していない理由です
ASP.NET MVCはカスタム404ページをあまりサポートしていません。カスタムコントローラファクトリ、キャッチオールルート、HandleUnknownAction
name__付きのベースコントローラクラス
IISのカスタムエラーページは、これまでのところより良い選択肢です。
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
</httpErrors>
</system.webServer>
public class ErrorController : Controller
{
public ActionResult PageNotFound()
{
Response.StatusCode = 404;
return View();
}
}
怠惰な人々のために:
Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0
それからglobal.asax
からこの行を削除します
GlobalFilters.Filters.Add(new HandleErrorAttribute());
これはIIS 7 +およびIIS Express専用です
Cassiniを使用しているのであれば、まあまあまあまあまあまあです…
私はこれが答えられたことを知っています。しかし、答えは本当に単純です(本当にこれに答えるために David Fowler and Damian Edwards に応援します)。
何もする必要はありません。
ASP.NET MVC3
の場合、すべてのビットと部分はそこにあります。
<system.web>
<customErrors mode="On" defaultRedirect="/ServerError">
<error statusCode="404" redirect="/NotFound" />
</customErrors>
そして
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
</httpErrors>
...
<system.webServer>
...
</system.web>
今、私が使うことに決めたルートを注意深く書き留めてください。あなたは何でも使うことができますが、私のルートは
/NotFound
< - 404が見つからない場合、エラーページ。/ServerError
< - その他のエラーについては、私のコードで発生したエラーを含めます。これは500 Internal Server Errorです。<system.web>
の最初のセクションにカスタムエントリが1つしかないことを確認してください。 statusCode="404"
エントリー? 500 Server Error
を含む他のすべてのエラー(つまり、コードにバグがあり、ユーザーの要求をクラッシュさせたときに発生する厄介なエラー)..その他のすべてのエラーは設定defaultRedirect="/ServerError"
で処理されるため、ステータスコードは1つだけです。 404ページが見つからない場合は、ルート/ServerError
に進んでください。
OK。 global.asax
にリストされている私の路線になりました。
これが私のフルルートセクションです。
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});
routes.MapRoute(
"Error - 404",
"NotFound",
new { controller = "Error", action = "NotFound" }
);
routes.MapRoute(
"Error - 500",
"ServerError",
new { controller = "Error", action = "ServerError"}
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional}
);
}
それは2つの無視ルート - > axd's
とfavicons
をリストします(あなたのために、ooo!ボーナスはルートを無視します!)それから私は2つの明白なエラー処理ルートを持っています。この場合、デフォルトのものです。もちろん、私にはもっとありますが、それは私のWebサイトにとって特別なことです。 エラールートがリストの一番上にあることを確認してください。順序は必須です。
最後に、global.asax
ファイルの中にいる間は、HandleError属性をグローバルに登録しません。いいえ、いいえ、いいえ先生。ナッダいや。ニエン。負。 Noooooooooo ...
global.asax
からこの行を削除
GlobalFilters.Filters.Add(new HandleErrorAttribute());
それでは、2つのアクションメソッドを持つコントローラを追加します。
public class ErrorController : Controller
{
public ActionResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
}
public ActionResult ServerError()
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
// Todo: Pass the exception into the view model, which you can make.
// That's an exercise, dear reader, for -you-.
// In case u want to pass it to the view, if you're admin, etc.
// if (User.IsAdmin) // <-- I just made that up :) U get the idea...
// {
// var exception = Server.GetLastError();
// // etc..
// }
return View();
}
// Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
public ActionResult ThrowError()
{
throw new NotImplementedException("Pew ^ Pew");
}
}
わかりました、これをチェックしましょう。まず、NO[HandleError]
属性があります。どうして?組み込みのASP.NET
フレームワークはすでにエラーを処理しているので、エラーを処理するために必要な作業はすべて指定しています。
次に、2つのアクション方法があります。難しいことは何もありません。例外情報を表示したい場合は、Server.GetLastError()
を使用してその情報を取得できます。
ボーナスWTF:はい、私はエラー処理をテストするために、3番目のアクションメソッドを作りました。
そして最後に、2つのビューを作成します。このコントローラーのために、emを通常のビュースポットに配置します。
Application_Error(object sender, EventArgs e)
は必要ありませんそしてそれは、私の友人たちもそれであるべきです。
さて、これを読んでおめでとうとユニコーンを賞品にしましょう!
私はA LOT MVCで404を適切に管理する方法(特にMVC3)について調べました、そして、これが、私がやってくる最良の解決策です。とアップ:
Global.asaxの場合:
public class MvcApplication : HttpApplication
{
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 404)
{
Response.Clear();
var rd = new RouteData();
rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
rd.Values["controller"] = "Errors";
rd.Values["action"] = "NotFound";
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
}
}
}
ErrorsController:
public sealed class ErrorsController : Controller
{
public ActionResult NotFound()
{
ActionResult result;
object model = Request.Url.PathAndQuery;
if (!Request.IsAjaxRequest())
result = View(model);
else
result = PartialView("_NotFound", model);
return result;
}
}
(オプション)
説明:
ご参考までに、ASP.NET MVC3アプリが404を生成することができる6つの異なるケースがあります。
(ASP.NET Frameworkによって自動的に生成されます。)
(1) URLがルートテーブルに一致しません。
(ASP.NET MVCフレームワークによって自動的に生成されます。)
(2)ルートテーブル内で一致するものが見つかりましたが、存在しないコントローラを指定しています。
(3)ルートテーブル内で一致するものが見つかりましたが、存在しないアクションを指定しています。
(手動で生成された:)
(4)アクションはメソッドHttpNotFound()を使用してHttpNotFoundResultを返します。
(5)アクションはステータスコード404のHttpExceptionをスローします。
(6)アクションはResponse.StatusCodeプロパティを手動で404に変更します。
通常、あなたは3つの目的を達成したいです:
(1)ユーザーに404エラーのカスタムページを表示します。
(2)クライアントの応答で404ステータスコードを維持します(SEOにとって特に重要です)。
(3) 302リダイレクトを伴わずに直接応答を送信します。
これを達成するためにさまざまな方法があります。
(1)
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customError>
</system.web>
この解決方法の問題:
(2)
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
この解決方法の問題:
(3)
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
この解決方法の問題:
(4)
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customError>
</system.web>
そして
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
この解決方法の問題:
以前にこれで悩んでいた人々は彼ら自身のライブラリを作成しようとさえしました( http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.htmlを見てください )。しかし、以前の解決策は、外部ライブラリを使用するという複雑さなしに、すべてのケースをカバーするようです。
私はcottsaksソリューションがとても好きで、それが非常に明確に説明されていると思います。私の唯一の追加は、次のようにステップ2を変更することでした
public abstract class MyController : Controller
{
#region Http404 handling
protected override void HandleUnknownAction(string actionName)
{
//if controller is ErrorController dont 'nest' exceptions
if(this.GetType() != typeof(ErrorController))
this.InvokeHttp404(HttpContext);
}
public ActionResult InvokeHttp404(HttpContextBase httpContext)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error");
errorRoute.Values.Add("action", "Http404");
errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
errorController.Execute(new RequestContext(
httpContext, errorRoute));
return new EmptyResult();
}
#endregion
}
基本的にこれは無効なアクションを含むURLとコントローラが例外ルーチンを2回トリガするのを防ぎます。例えばasdfsdf/dfgdfgdのようなURLの場合
@ cottsakのメソッドを無効なコントローラに対して機能させる唯一の方法は、CustomControllerFactoryの既存のルートリクエストを変更することです。
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
else
return ObjectFactory.GetInstance(controllerType) as Controller;
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
requestContext.RouteData.Values["controller"] = "Error";
requestContext.RouteData.Values["action"] = "Http404";
requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);
return ObjectFactory.GetInstance<ErrorController>();
}
else
throw ex;
}
}
}
私はMVC 2.0を使用していることに言及する必要があります。
これはMVCツールを使用したもう1つの方法です。これは、不正なコントローラ名、不正なルート名、およびActionメソッド内に収まると思われるその他の基準に対する要求を処理できます。個人的には、302/200リダイレクトを行い、Razorビューを使用したResponseRewrite(Server.Transfer
)をサポートしていないため、できるだけ多くのweb.config設定を避けることをお勧めします。私はSEOの理由でカスタムエラーページで404を返すことを望みます。
これのいくつかは上記のcottsakのテクニックを新しく採用したものです。
このソリューションでは、代わりにMVC 3エラーフィルタを優先する最小限のweb.config設定も使用します。
アクションまたはカスタムのActionFilterAttributeからHttpExceptionをスローするだけです。
Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")
次の設定をweb.configに追加してください。これはMVCのHandleErrorAttributeを使うために必要です。
<customErrors mode="On" redirectMode="ResponseRedirect" />
HTTPエラーを除いて、MVCフレームワークのHandleErrorAttributeに似たカスタムHandleHttpErrorAttributeを追加します。
<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
Inherits FilterAttribute
Implements IExceptionFilter
Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"
Private m_HttpCode As HttpStatusCode
Private m_Master As String
Private m_View As String
Public Property HttpCode As HttpStatusCode
Get
If m_HttpCode = 0 Then
Return HttpStatusCode.NotFound
End If
Return m_HttpCode
End Get
Set(value As HttpStatusCode)
m_HttpCode = value
End Set
End Property
Public Property Master As String
Get
Return If(m_Master, String.Empty)
End Get
Set(value As String)
m_Master = value
End Set
End Property
Public Property View As String
Get
If String.IsNullOrEmpty(m_View) Then
Return String.Format(m_DefaultViewFormat, Me.HttpCode)
End If
Return m_View
End Get
Set(value As String)
m_View = value
End Set
End Property
Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
If filterContext Is Nothing Then Throw New ArgumentException("filterContext")
If filterContext.IsChildAction Then
Return
End If
If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
Return
End If
Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
Return
End If
If ex.GetHttpCode <> Me.HttpCode Then
Return
End If
Dim controllerName As String = filterContext.RouteData.Values("controller")
Dim actionName As String = filterContext.RouteData.Values("action")
Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)
filterContext.Result = New ViewResult With {
.ViewName = Me.View,
.MasterName = Me.Master,
.ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
.TempData = filterContext.Controller.TempData
}
filterContext.ExceptionHandled = True
filterContext.HttpContext.Response.Clear()
filterContext.HttpContext.Response.StatusCode = Me.HttpCode
filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
End Sub
End Class
GlobalFilters.Filters
のGlobalFilterCollection(Global.asax
)にフィルタを追加します。この例では、すべてのInternalServerError(500)エラーをエラー共有ビュー(Views/Shared/Error.vbhtml
)にルーティングします。 NotFound(404)エラーは共有ビューのErrorHttp404.vbhtmlにも送信されます。ここで401エラーを追加して、これを追加のHTTPエラーコードに対してどのように拡張できるかを説明します。これらは共有ビューである必要があり、それらはすべてSystem.Web.Mvc.HandleErrorInfo
オブジェクトをモデルとして使用しています。
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})
基本コントローラクラスを作成し、それをコントローラ内で継承します。このステップにより、未知のアクション名を処理してHTTP 404エラーをHandleHttpErrorAttributeに上げることができます。
Public Class BaseController
Inherits System.Web.Mvc.Controller
Protected Overrides Sub HandleUnknownAction(actionName As String)
Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
End Sub
Public Function Unknown() As ActionResult
Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
Return New EmptyResult
End Function
End Class
ControllerFactoryオーバーライドを作成して、Application_StartのGlobal.asaxファイルでオーバーライドします。このステップにより、無効なコントローラ名が指定されたときにHTTP 404例外を発生させることができます。
Public Class MyControllerFactory
Inherits DefaultControllerFactory
Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
Try
Return MyBase.GetControllerInstance(requestContext, controllerType)
Catch ex As HttpException
Return DependencyResolver.Current.GetService(Of BaseController)()
End Try
End Function
End Class
'In Global.asax.vb Application_Start:
controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)
BaseController UnknownアクションのためのRoutTable.Routesに特別なルートを含めます。これは、ユーザが未知のコントローラや未知のアクションにアクセスした場合に404を発生させるのに役立ちます。
'BaseController
routes.MapRoute( _
"Unknown", "BaseController/{action}/{id}", _
New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)
この例では、MVCフレームワークを使用して、フィルタ属性と共有エラービューを使用してリダイレクトせずに404 HTTPエラーコードをブラウザに返す方法を示しました。また、無効なコントローラ名とアクション名が指定された場合に同じカスタムエラーページを表示する方法も示します。
無効なコントローラ名、アクション名、およびHome/TriggerNotFoundアクションから発生したカスタム404のスクリーンショットを追加して、one =を投稿できます。このソリューションを使用して次のURLにアクセスすると、Fiddlerは404メッセージを返します。
/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound
上記のcottsakの投稿とこれらの記事は参考になりました。
未処理の領域、コントローラ、およびアクションを扱う私の短縮ソリューション。
ビュー404.cshtmlを作成します。
コントローラの基本クラスを作成します。
public class Controller : System.Web.Mvc.Controller
{
protected override void HandleUnknownAction(string actionName)
{
Http404().ExecuteResult(ControllerContext);
}
protected virtual ViewResult Http404()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View("404");
}
}
ベースコントローラーをフォールバックとして返すカスタムコントローラーファクトリを作成します。
public class ControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
return base.GetControllerInstance(requestContext, controllerType);
return new Controller();
}
}
次の行をApplication_Start()
に追加します。
ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
MVC 4では、WebAPI 404は次のように処理できます。
コースエイピココントローラー
// GET /api/courses/5
public HttpResponseMessage<Courses> Get(int id)
{
HttpResponseMessage<Courses> resp = null;
var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();
resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);
return resp;
}
ホームコントローラー
public ActionResult Course(int id)
{
return View(id);
}
見る
<div id="course"></div>
<script type="text/javascript">
var id = @Model;
var course = $('#course');
$.ajax({
url: '/api/courses/' + id,
success: function (data) {
course.text(data.Name);
},
statusCode: {
404: function()
{
course.text('Course not available!');
}
}
});
</script>
グローバル
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
結果
NotFoundMVCを試してみてください。それは動作します、セットアップなし。
標準的なCustomErrors
の設定うまくいくはずですしかし、Server.Transfer
に依存しているため、ResponseRewrite
の内部実装はMVCと互換性がないようです。
これは私にとっては明白な機能上の穴のように感じられるので、私はHTTPモジュールを使用してこの機能を再実装することにしました。以下の解決策では、通常の場合と同じように、有効なMVCルートにリダイレクトすることで、HTTPステータスコード(404を含む)を処理できます。
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="404.aspx" />
<error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>
これは以下のプラットフォームでテストされています。
メリット
解決策
namespace Foo.Bar.Modules {
/// <summary>
/// Enables support for CustomErrors ResponseRewrite mode in MVC.
/// </summary>
public class ErrorHandler : IHttpModule {
private HttpContext HttpContext { get { return HttpContext.Current; } }
private CustomErrorsSection CustomErrors { get; set; }
public void Init(HttpApplication application) {
System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");
application.EndRequest += Application_EndRequest;
}
protected void Application_EndRequest(object sender, EventArgs e) {
// only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {
int statusCode = HttpContext.Response.StatusCode;
// if this request has thrown an exception then find the real status code
Exception exception = HttpContext.Error;
if (exception != null) {
// set default error status code for application exceptions
statusCode = (int)HttpStatusCode.InternalServerError;
}
HttpException httpException = exception as HttpException;
if (httpException != null) {
statusCode = httpException.GetHttpCode();
}
if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {
Dictionary<int, string> errorPaths = new Dictionary<int, string>();
foreach (CustomError error in CustomErrors.Errors) {
errorPaths.Add(error.StatusCode, error.Redirect);
}
// find a custom error path for this status code
if (errorPaths.Keys.Contains(statusCode)) {
string url = errorPaths[statusCode];
// avoid circular redirects
if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {
HttpContext.Response.Clear();
HttpContext.Response.TrySkipIisCustomErrors = true;
HttpContext.Server.ClearError();
// do the redirect here
if (HttpRuntime.UsingIntegratedPipeline) {
HttpContext.Server.TransferRequest(url, true);
}
else {
HttpContext.RewritePath(url, false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext);
}
// return the original status code to the client
// (this won't work in integrated pipleline mode)
HttpContext.Response.StatusCode = statusCode;
}
}
}
}
}
public void Dispose() {
}
}
}
使用法
これをweb.configの最後のHTTPモジュールとして含めます
<system.web>
<httpModules>
<add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
</httpModules>
</system.web>
<!-- IIS7+ -->
<system.webServer>
<modules>
<add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
</modules>
</system.webServer>
あなたが注意を向けているのなら、Integrated PipelineモードではServer.TransferRequest
が動く方法のためこれが常にHTTP 200で応答することに気づくでしょう。適切なエラーコードを返すには、次のエラーコントローラを使用します。
public class ErrorController : Controller {
public ErrorController() { }
public ActionResult Index(int id) {
// pass real error code to client
HttpContext.Response.StatusCode = id;
HttpContext.Response.TrySkipIisCustomErrors = true;
return View("Errors/" + id.ToString());
}
}
私の解決策は、誰かがそれを役に立つと思った場合に備えて。
Web.configで:
<system.web>
<customErrors mode="On" defaultRedirect="Error" >
<error statusCode="404" redirect="~/Error/PageNotFound"/>
</customErrors>
...
</system.web>
Controllers/ErrorController.cs
内:
public class ErrorController : Controller
{
public ActionResult PageNotFound()
{
if(Request.IsAjaxRequest()) {
Response.StatusCode = (int)HttpStatusCode.NotFound;
return Content("Not Found", "text/plain");
}
return View();
}
}
Shared
フォルダーにPageNotFound.cshtml
を追加してください。
ASP.NET MVCのエラーに対処することは、単に悩みの種です。私はこのページや他の質問やサイトでたくさんの提案を試みましたが、何もうまくいきません。 1つの提案はweb.config inside system.webserverでエラーを処理することでしたが、それは空白ページを返すだけです。
この解決策を思いつくときの私の目標は、
これが私の解決策です。
1。system.web sectionに以下を追加
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="~/Error/404.aspx" />
<error statusCode="500" redirect="~/Error/500.aspx" />
</customErrors>
<system.web>
上記では、routes.configで処理されていないURLと未処理の例外、特にビューで発生したものを処理します。私がaspx not htmlを使っていないことに注意してください。これは、背後のコードにレスポンスコードを追加できるようにするためです。
2。プロジェクトのルートにErrorという名前のフォルダーを作成し、2つのWebフォームを追加します。以下が私の404ページです。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title >Page Not found</title>
<link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
</head>
<body>
<div class="top-nav">
<a runat="server" class="company-logo" href="~/"></a>
</div>
<div>
<h1>404 - Page Not found</h1>
<p>The page you are looking for cannot be found.</p>
<hr />
<footer></footer>
</div>
</body>
</html>
そしてその背後にあるコードでレスポンスコードを設定しました
protected void Page_Load(object sender, EventArgs e)
{
Response.StatusCode = 404;
}
500ページに同じことをする
。コントローラ内のエラーを処理します。やり方はたくさんあります。これは私のために働いたものです。私のコントローラーはすべて基本コントローラーから継承します。ベースコントローラには、以下のメソッドがあります。
protected ActionResult ShowNotFound()
{
return ShowNotFound("Page not found....");
}
protected ActionResult ShowNotFound(string message)
{
return ShowCustomError(HttpStatusCode.NotFound, message);
}
protected ActionResult ShowServerError()
{
return ShowServerError("Application error....");
}
protected ActionResult ShowServerError(string message)
{
return ShowCustomError(HttpStatusCode.InternalServerError, message);
}
protected ActionResult ShowNotAuthorized()
{
return ShowNotAuthorized("You are not allowed ....");
}
protected ActionResult ShowNotAuthorized(string message)
{
return ShowCustomError(HttpStatusCode.Forbidden, message);
}
protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
{
Response.StatusCode = (int)statusCode;
string title = "";
switch (statusCode)
{
case HttpStatusCode.NotFound:
title = "404 - Not found";
break;
case HttpStatusCode.Forbidden:
title = "403 - Access Denied";
break;
default:
title = "500 - Application Error";
break;
}
ViewBag.Title = title;
ViewBag.Message = message;
return View("CustomError");
}
4。CustomError.cshtmlをShared viewsフォルダに追加してください。以下が私のものです。
<h1>@ViewBag.Title</h1>
<br />
<p>@ViewBag.Message</p>
これであなたのアプリケーションコントローラであなたはこのようなことをすることができます。
public class WidgetsController : ControllerBase
{
[HttpGet]
public ActionResult Edit(int id)
{
Try
{
var widget = db.getWidgetById(id);
if(widget == null)
return ShowNotFound();
//or return ShowNotFound("Invalid widget!");
return View(widget);
}
catch(Exception ex)
{
//log error
logger.Error(ex)
return ShowServerError();
}
}
}
さて、警告のために。静的ファイルエラーは処理されません。したがって、example.com/widgetsなどのルートがあり、ユーザーがそれをexample.com/widgets.htmlに変更すると、IISが表示されます。デフォルトのエラーページなので、他の方法でIISレベルのエラーを処理する必要があります。
私のコメントが長すぎたので答えを投稿しています...
Unicornの投稿/回答に対するコメントと質問の両方です。
https://stackoverflow.com/a/7499406/687549
それが単純であること、そしてどうやらマイクロソフトの何人かの人々が相談されたという事実のために、私は他のものよりこの答えを好む。私は3つの質問を受けました、そしてそれらが答えられることができるならば、私はこの答えをASP.NET MVC(x)アプリのためのinterwebsの上のすべての404/500エラー答えの聖杯と呼びます。
@ Pure.Krome
GWBによって指摘されたコメントからSEOのものであなたの答えを更新することができますか(あなたの答えの中でこれについて言及することは決してありませんでした) - <customErrors mode="On" redirectMode="ResponseRewrite">
と<httpErrors errorMode="Custom" existingResponse="Replace">
?
そのようにしても大丈夫かどうか、ASP.NETチームの友人に尋ねてもらえますか - 確認を得られればいいのですが - SEOでうまく遊べるようにするためにredirectMode
とexistingResponse
を変更するのは大したことではありません!
あなたがマイクロソフトで友達と話した後に、あなたがそのすべてのもの(誰かが示唆するようにcustomErrors redirectMode="ResponseRewrite"
、customErrors redirectMode="ResponseRedirect"
、httpErrors errorMode="Custom" existingResponse="Replace"
、REMOVE customErrors
COMPLETELY)を取り巻く明確化を加えることができますか?
私が言っていたように;これは54 000件以上のビューを含む非常に人気のある質問のようであるため、我々はあなたの答えをより完全にすることができればそれはすばらしいでしょう。
更新:Unicornの回答は302 Foundおよび200 OKであり、ルートを使用して404のみを返すように変更することはできません。それはあまりMVCではない物理ファイルでなければなりません。だから別の解決策に移ってください。これは究極のMVCであると思われるため、残念です。
Herman Kanのものとほとんど同じですが、私のプロジェクトでうまくいくように小さなしわを付けて私のソリューションを追加します。
カスタムエラーコントローラを作成します。
public class Error404Controller : BaseController
{
[HttpGet]
public ActionResult PageNotFound()
{
Response.StatusCode = 404;
return View("404");
}
}
次にカスタムコントローラファクトリを作成します。
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
}
}
最後に、カスタムエラーコントローラにオーバーライドを追加します。
protected override void HandleUnknownAction(string actionName)
{
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error404");
errorRoute.Values.Add("action", "PageNotFound");
new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
}
以上です。 Web.configを変更する必要はありません。
1)抽象Controllerクラスを作ります。
public abstract class MyController:Controller
{
public ActionResult NotFound()
{
Response.StatusCode = 404;
return View("NotFound");
}
protected override void HandleUnknownAction(string actionName)
{
this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
}
protected override void OnAuthorization(AuthorizationContext filterContext) { }
}
2)あなたのすべてのコントローラでこの抽象クラスから継承する
public class HomeController : MyController
{}
3)View-Sharedフォルダに "NotFound"という名前のビューを追加します。
私はこのスレッドに投稿された解決策のほとんどを通過しました。この質問は古くなっているかもしれませんが、今でもまだ新しいプロジェクトに非常に当てはまります。そのため、ここで提示されている回答やその他の箇所についてはかなりの時間をかけて読んでいます。
@Marcoが404が発生する可能性があるさまざまなケースを指摘したので、私はそのリストに対してまとめたソリューションをチェックしました。彼の要件リストに加えて、もう1つ追加しました。
この解決策は2倍です。
その最初の部分は@Guillaumeから来ています https://stackoverflow.com/a/27354140/2310818 。彼らの解決策は無効な経路、無効なコントローラと無効なアクションが原因で発生したあらゆる404の世話をします。
そのアイデアは、Webフォームを作成してから、それをMVC Errors ControllerのNotFoundアクションと呼ばせることです。あなたはFiddlerに一つの302を見ることはないでしょうから、それはいかなるリダイレクトもなしにこれの全てをします。元のURLも保持されるため、このソリューションは素晴らしいものになります。
その2番目の部分は@Germánから来ています https://stackoverflow.com/a/5536676/2310818 。彼らの解決策は、HttpNotFoundResult()の形であなたのアクションによって返される404を処理するか、または新しいHttpException()をスローします!
そのアイデアは、MVCコントローラーによってスローされたレスポンスと例外をフィルターで調べて、Errors Controllerで適切なアクションを呼び出すことです。このソリューションもリダイレクトなしで機能し、元のURLは保持されます。
ご覧のとおり、これらのソリューションは両方とも非常に堅牢なエラー処理メカニズムを提供し、@ Marcoにリストされているすべての要件と私の要件を満たしています。このサンプルの実用的なサンプルやデモをご覧になりたい場合は、コメント欄に記入してください。
すべての記事を読み終えましたが、私には何も機能しません。私の要件ユーザーには、URLのカスタム404ページに何かを入力してください。404の処理を正しく理解する必要があります。
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="~/PageNotFound.aspx"/>
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
私はこの記事が非常に参考になったことがわかりました。すぐに読むべきです。 カスタマーエラーページ - Ben Foster