割と有名な ASP.NET のフォーム認証モジュールのおせっかい機能として、HTTP ステータスコードで 401 を返すと自動的にログインページへの 302 リダイレクトに変換するというのがあります。
認証が必要な Web サイトを作る場合には割と便利なんですが、API を MVC でサクッと作りたい場合には厄介です。API としては 401 を返したいのに、勝手にログインページへのリダイレクトになるからです。
ちなみに ASP.NET Web API の Nightly 版でも同じ問題がありましたが、製品版ではアプリケーション設定に特殊なキーを追加することで回避可能になりました。
最初から Web API で作っとけよという感じですが、今回久しぶりにはまったので書きます。
ASP.NET 4.5 から HttpResponse に SuppressFormsAuthenticationRedirect というそのまんまな名前のプロパティが追加されています。
HttpResponse.SuppressFormsAuthenticationRedirect プロパティ (System.Web)
このプロパティを true にすればリダイレクトは回避出来るんですが、このプロパティに値をセットするタイミングで少し悩みました。
最初は OnActionExecuting をオーバーライドして設定すればいいだろと思って試しました。
public class ApiController : Controller { protected override void OnActionExecuting(ActionExecutingContext filterContext) { Response.SuppressFormsAuthenticationRedirect = true; base.OnActionExecuting(filterContext); } [Authorize] public ActionResult Auth() { return Json(true, JsonRequestBehavior.AllowGet); } }
ところが実際に試すとリダイレクトされたままです。
少し考えると、そもそも Authorize 属性を付けた場合には OnActionExecuting が実行される前に処理が打ち切られてしまうので、そもそも呼び出されないことに気が付きました。
public class ApiController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { requestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; base.Initialize(requestContext); } [Authorize] public ActionResult Auth() { return Json(true, JsonRequestBehavior.AllowGet); } }
必ず呼び出されるメソッドを考えた結果、今回は Initialize をオーバーライドすることにしました。
これで 401 を返してもリダイレクトされることが無くなりました。