ASP.NET MVCのコントローラーからスローされた例外を正しく処理するにはどうすればよいですか? HandleError
属性は、MVCインフラストラクチャによってスローされた例外のみを処理し、自分のコードによってスローされた例外は処理しないようです。
このweb.configを使用する
<customErrors mode="On">
<error statusCode="401" redirect="/Errors/Http401" />
</customErrors>
次のコードで
namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
// Force a 401 exception for testing
throw new HttpException(401, "Unauthorized");
}
}
}
私が望んでいたものにはなりません。代わりに、web.configを変更して実際のエラー情報を確認するよう指示する一般的なASP.NETエラーページが表示されます。ただし、例外をスローする代わりに無効なビューを返すと、/ Shared/Views/Error.aspxページが表示されます。
return View("DoesNotExist");
上記のようにコントローラー内で例外をスローすると、すべてのHandleError
機能がバイパスされるようです。エラーページを作成する正しい方法は何ですか。また、MVCインフラストラクチャでNiceをプレイするにはどうすればよいですか。
Kazimanzurrashaidのおかげで、Global.asax.csで次のようになりました。
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
HttpException httpException = unhandledException as HttpException;
if (httpException == null)
{
Exception innerException = unhandledException.InnerException;
httpException = innerException as HttpException;
}
if (httpException != null)
{
int httpCode = httpException.GetHttpCode();
switch (httpCode)
{
case (int) HttpStatusCode.Unauthorized:
Response.Redirect("/Http/Error401");
break;
}
}
}
サポートする必要がある追加のHTTPエラーコードに基づいて、HttpContollerにさらにページを追加できます。
Controller.OnException(ExceptionContext context)
。それをオーバーライドします。
protected override void OnException(ExceptionContext filterContext)
{
// Bail if we can't do anything; app will crash.
if (filterContext == null)
return;
// since we're handling this, log to elmah
var ex = filterContext.Exception ?? new Exception("No further information exists.");
LogException(ex);
filterContext.ExceptionHandled = true;
var data = new ErrorPresentation
{
ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
TheException = ex,
ShowMessage = !(filterContext.Exception == null),
ShowLink = false
};
filterContext.Result = View("ErrorPage", data);
}
HandleError属性は、MVCインフラストラクチャによってスローされた例外のみを処理し、自分のコードによってスローされた例外は処理しないようです。
それはただ間違っています。実際、HandleErrorは、独自のコードまたは独自のコードによって呼び出されたコードでスローされた例外のみを「処理」します。言い換えれば、アクションが呼び出しスタックにある例外のみです。
表示されている動作の実際の説明は、スローしている特定の例外です。 HandleErrorの動作は、HttpExceptionでは異なります。ソースコードから:
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
HandleError属性を持つHttpCodeに基づいて特定のErrorPageを表示できるとは思わないので、この目的のためにHttpModuleを使用したいと思います。特定のエラーごとに異なるページが存在するフォルダー「ErrorPages」があり、マッピングが通常のWebフォームアプリケーションと同じweb.configで指定されていると仮定します。また、次はエラーページを表示するために使用されるコードです。
public class ErrorHandler : BaseHttpModule{
public override void OnError(HttpContextBase context)
{
Exception e = context.Server.GetLastError().GetBaseException();
HttpException httpException = e as HttpException;
int statusCode = (int) HttpStatusCode.InternalServerError;
// Skip Page Not Found and Service not unavailable from logging
if (httpException != null)
{
statusCode = httpException.GetHttpCode();
if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable))
{
Log.Exception(e);
}
}
string redirectUrl = null;
if (context.IsCustomErrorEnabled)
{
CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors");
if (section != null)
{
redirectUrl = section.DefaultRedirect;
if (httpException != null)
{
if (section.Errors.Count > 0)
{
CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)];
if (item != null)
{
redirectUrl = item.Redirect;
}
}
}
}
}
context.Response.Clear();
context.Response.StatusCode = statusCode;
context.Response.TrySkipIisCustomErrors = true;
context.ClearError();
if (!string.IsNullOrEmpty(redirectUrl))
{
context.Server.Transfer(redirectUrl);
}
}
}
これを読んでいる他の人が経験している可能性のあるもう1つの可能性(あなたの場合は真実ではありません)は、エラーページがエラーを投げているか、実装していないことです:
System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>
この場合、デフォルトのエラーページが表示されます(そうしないと、カスタムエラーページに自分自身を送信しようとするため、無限ループが発生します)。これはすぐには分かりませんでした。
このモデルは、エラーページに送信されるモデルです。エラーページでサイトの他の部分と同じマスターページを使用し、他のモデル情報が必要な場合は、独自の[HandleError]
タイプの属性を作成するか、OnException
などをオーバーライドする必要があります。
Controller.OnException()アプローチを選択しました。これは私にとって論理的な選択です。ASP.NETMVCを選択したため、フレームワークレベルにとどまり、可能な場合は基礎となるメカニズムをいじることを避けます。
次の問題に遭遇しました。
ビュー内で例外が発生すると、そのビューからの部分的な出力がエラーメッセージとともに画面に表示されます。
FilterContext.Resultを設定する前に、応答をクリアすることでこれを修正しました-このように:
filterContext.HttpContext.Response.Clear(); // gets rid of any garbage
filterContext.Result = View("ErrorPage", data);
これが誰か他の人の時間を節約することを願っています:-)
protected override void OnException (ExceptionContext filterContext )
{
if (filterContext != null && filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
this.View("Error").ViewData["Exception"] = filterContext.Exception.Message;
this.View("Error").ExecuteResult(this.ControllerContext);
}
}
Jeff Atwoodの ユーザーフレンドリーな例外処理モジュール はMVCに最適です。 MVCプロジェクトのソースコードをまったく変更せずに、web.configで完全に構成できます。ただし、200
ステータスではなく元のHTTPステータスを返すには、わずかな変更が必要です。これに関連する フォーラム投稿 を参照してください。
基本的に、Handler.vbでは、次のようなものを追加できます。
' In the header...
Private _exHttpEx As HttpException = Nothing
' At the top of Public Sub HandleException(ByVal ex As Exception)...
HttpContext.Current.Response.StatusCode = 500
If TypeOf ex Is HttpException Then
_exHttpEx = CType(ex, HttpException)
HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode()
End If