ASP.NET MVC - フォーム認証のタイムアウトで、ログインページにリダイレクトしないようにする(.NET 4.5~)
先日 MSDN で偶然このプロパティを見つけました。
HttpResponse.SuppressFormsAuthenticationRedirect プロパティ (System.Web)
思わず、おおーとなりました。
なぜかと言うと、MSDN の解説にも書いてありますが、ASP.NET では、認証されていない状態(認証クッキーのタイムアウトなども)で認証が必要な URL にアクセスすると、ログインページにリダイレクトされます。
この動きは AJAX リクエストだと割とやっかいで、レスポンスに JSON を期待しているのに取得できるのはログインページの HTML なので、スクリプトエラーになったり、部分的な HTML を期待してページの一部を書き換える処理だと、ページの一部がログインページになったりってことがありました。
今まではこの設定がなかったので、ログインのアクションメソッドで AJAX リクエストか判断して小細工をした記憶があります。
ということで、早速このプロパティを試してみました。
ログインページにリダイレクトする動き
まずは、今まで通りのログインページにリダイレクトする動きを確認します。
コントローラ
public class SampleController : Controller { // テスト用ページを表示するアクション public ActionResult Index() { return View(); } // 認証が必要なアクション [Authorize] public ActionResult Something() { return Json( new { message = "特に意味のない JSON" }, JsonRequestBehavior.AllowGet); } }
ビュー(のJavaScript部分)
index.cshtmlにこのスクリプトが書いてあります。
$(function() { // 認証が必要なアクションへの AJAX リクエスト $.ajax("/sample/something") .done(function(data, status, xhr) { console.log("done"); }) .fail(function(xhr, status, error) { console.log("fail:" + status + ", " + error); }); });
/sample/indexに対するHTTPの通信を確認すると、リダイレクト(302)が返ってくることがわかります。
XMLHttpRequest は内部でリダイレクトを処理するようで、最終的には$.ajax
のdone
に渡したコールバックが呼び出されて、引数data
にはログインページ(/account/login)のHTMLが入ります。
ログインページにリダイレクトしないようにする
今度は、ログインページにリダイレクトしないように、SuppressFormsAuthenticationRedirect
プロパティを使ってみます。
設定するタイミングや方法はいろいろあるかなと思いますが、今回は試しにAuthorizeAttribute
を継承した属性を作ってみました。
属性とコントローラ
public class SampleAuthorizeAttribute : AuthorizeAttribute { // 認証されていないリクエストを処理するメソッドをオーバーライド protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { var httpContext = filterContext.HttpContext; // AJAX リクエストの場合は、ログインページへのリダイレクトを抑制する if(httpContext.Request.IsAjaxRequest()) { httpContext.Response.SuppressFormsAuthenticationRedirect = true; } base.HandleUnauthorizedRequest(filterContext); } } public class SampleController : Controller { public ActionResult Index() { return View(); } // Authorize 属性から SampleAuthorize 属性に変更 [SampleAuthorize] public ActionResult Something() { return Json( new { message = "特に意味のない JSON" }, JsonRequestBehavior.AllowGet); } }
さっきと同じように AJAX リクエストの通信を見てみると、今度はリダイレクトではなく 401 が返ってきます。
また、$.ajax
のfail
に渡したコールバックが呼び出されて、console.log は以下のように。
fail:error, Unauthorized
JavaScript で素直にエラー処理を書けるようになるので素敵ですね。