ASP.NET WEB API には、エラー用フィルター属性のインターフェイス、
System.Web.Http.Filters.IExceptionFilter
が用意されています。
これを使えば、(MVC と似たように)アクションメソッド内でエラーが起きた時に、
独自の処理を挟むことができます。
今日は、これについてです。
基本的な使い方は、Exception Handling in ASP.NET Web API
に書かれています。
デフォルトでは
デフォルトの状態(エラー用フィルター属性を使わない状態)では、
アクションメソッド内でエラーが起きた場合は、
500 Internal Server Error が返ってくるようになっています。
public class ValuesController : ApiController { // GET api/values public IEnumerable<string> Get() { throw new Exception("エラーです"); } }
これはフレームワーク内で用意されている動きなので、
MVC の用に、”filters.Add(new HandleErrorAttribute());” といったように
書かなくても、500 Internal Server Error が返ってきます。
エラーの詳細を見せるか設定する
本題から話がずれますが、このデフォルトのレスポンスについて少し。
先ほど帰ってきたレスポンスの中には、スタックトレース等、
外部には公開したくない情報まで記載されています。
この詳細は、WebConfig の customErrors の mode を
On (または RemoteOnly)にすることで、隠すことができます。
// Web.config <system.web> <customErrors mode="On"> </customErrors> ...
↑スタックトレース等が見えなくなった。(つД⊂)
この動きは、WebApiConfig.cs の中の HttpConfiguration の IncludeErrorDetailPolicy で変更することができます。
public static class WebApiConfig { public static void Register(HttpConfiguration config) { //... config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Default; } }
IncludeErrorDetailPolicy は Enum で、
Default, LocalOnly, Always, Never の4つを指定できます。
デフォルトは "Default" が設定されており、
先ほど説明したように、WebConfig の CustomErros の値に依存するようになっています。
エラーカスタム処理
本題のエラーに対する処理についてです。
エラーが起きたら、ログを取ったりメールを送信したり・・・と
自分でエラーを処理したい場合は、エラー用のフィルター属性を作成して
実現することができます。
System.Web.Http.Filters.IExceptionFilter を実装した属性を作成し、
適用したい範囲(Controller, Method, Global)に属性を適用させます。
実装
大抵、IExceptionFilter を一から実装する必要はなく、
すでに用意されている ExceptionFilterAttribute クラスを継承して作成します。
public class MyExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) { if (actionExecutedContext.Exception is NotImplementedException) { actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented); } } }
上のコードは、NotImplementedException が throw された時、
501 NotImplemented を返すようにした例です。
actionExecutedContext.Exception に例外が格納されているので、
ここで独自の処理を挟むことができます。
属性を作成した後は、適用したい範囲に合わせて
属性を付与する必要があります。
// とあるメソッドのみに適用したい場合 [MyExceptionFilter] public IEnumerable<string> Get() { //.. }
これで、Get() のメソッド内で NotImplementedException が throw された時、
501 NotImplemented が返ってくるようになります。
HttpResponseException は無視される
このエラー用フィルター属性ですが、HttpResponseException の場合は無視されます。
[MyExceptionFilter] public void Post([FromBody]string value) { //.... if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } }
上の場合、メソッド内で、HttpResponseException が throw されていますが、
[MyExceptionFilter] はスルーされ、属性内に書いた独自のエラー処理は実行されません。