ASP.NET MVC 5 の Ajax 通信でページを部分更新する際に、どのように例外を処理すべきかを悩んだので、まとめておきます。
現象
Ajax 通信で PartialView を返すアプリをシンプルなコードで実装します。
#HomeController.cs public class HomeController : Controller { public ActionResult Ajax() { ViewBag.Message = "Ajax Test page."; return View(); } [HttpPost] public ActionResult AjaxTest() { if (Request.IsAjaxRequest()) { return PartialView("_AjaxResult"); } return Content("Ajax 通信以外のアクセスはできません。"); } }
#_AjaxResult.cshtml <h3 class="text-success">Ajax 通信で返却された部分ビュー</h3>
#Ajax.cshtml <h2>AjaxTest</h2> @Html.TextBox("test", "Test", new { type = "button", @class = "btn btn-default" }) <div id="ajax-result"></div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") <script> $(function () { $('#test').click(function () { $.ajax({ type: 'POST', url: 'AjaxTest' }) .done(function (data, textStatus, jqXHR) { $('#ajax-result').html(data); }); }) }) </script> }
Test ボタンを押すと、「/Home/AjaxTest」にリクエストが送信され、ページが部分更新されます。
例えば、Form 認証でタイムアウトが発生すると、部分ビューの領域にログインページが表示されてしまいます。
また、コントローラーのアクションメソッドで Exception が発生しても、エラーページには遷移しません。
対応
Form 認証でタイムアウトが発生した際にログインページへ遷移されるのは、Web.config で「/Account/Login/」へのリダイレクトを設定しているからです。Ajax 通信の場合は、リダイレクトされたログインページを表示する領域が部分ビューになっているため、上記の現象が発生していました。そのため、Webconfig の設定に頼らずに、View から Ajax 通信したリクエストが返ってきたタイミングでステータスコードを判断して、リダイレクトする方法を取りました。
#Ajax.cshtml @section Scripts { @Scripts.Render("~/bundles/jqueryval") <script> $(function () { $('#test').click(function () { $.ajax({ type: 'POST', url: 'AjaxTest' }) .done(function (data, textStatus, jqXHR) { $('#ajax-result').html(data); }) .fail(function (jqXHR, textStatus, errorThrown) { if (jqXHR.status == 401) { $(location).attr("href", "/Account/Login/"); } else { $(location).attr("href", "/Error/AjaxError/"); } }); }) }) </script> }
Ajax 通信の場合は、Location ヘッダーにログインページの URL が指定された HttpStatusCode.Found(302)ではなく、HttpStatusCode.Unauthorized(401)を返して欲しいので、前回の投稿で書いた SuppressFormsAuthenticationRedirect プロパティを設定します。
#HomeController.cs public class HomeController : Controller { protected override void Initialize(RequestContext requestContext) { if (requestContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest") { requestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; } base.Initialize(requestContext); } }
ログインページへのリダイレクトについては、ここまでの実装で対応できますが、エラーページの遷移にも対応させるため、Ajax 通信用のアクションメソッドを追加します。
#ErrorController.cs [AllowAnonymous] public class ErrorController : Controller { public ActionResult AjaxError() { return View("Error"); } }
まとめ
部分更新ではない通常のページは、Web.config のリダイレクトの設定だけで Form 認証のタイムアウトに対応できますし、HandleErrorAttribute のフィルター属性を全体に適用することで、エラーページに遷移させることができます。
Ajax 通信でページを部分更新する際には、クライアント(View)側で個別に対応しなくてはならないのが面倒ですが、仕方ないのかもしれません。やり方はいろいろあると思いますので、ひとつの例として参考までに。