در ASP.NET MVC
به کمک فیلتر Authorize میتوان کاربران را در صورت درخواست دسترسی به کنترلر و یا اکشن متد خاصی در صورت لزوم و عدم اعتبارسنجی کامل، به صفحه لاگین هدایت کرد. این مساله در حین postback کامل به سرور به صورت خودکار رخ داده و کاربر به Login Url ذکر شده در web.config هدایت میشود. اما در مورد اعمال Ajax ایی چطور؟ در این حالت خاص، فیلتر Authorize قابلیت هدایت خودکار کاربران را به صفحه لاگین، ندارد. در ادامه نحوه رفع این نقیصه را بررسی خواهیم کرد.
تهیه فیلتر سفارشی SiteAuthorize
برای بررسی اعمال Ajaxایی، نیاز است فیلتر پیش فرض Authorize سفارشی شود:
using System;
using System.Net;
using System.Web.Mvc;
namespace MvcApplication28.Helpers
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public sealed class SiteAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
throw new UnauthorizedAccessException(); //to avoid multiple redirects
}
else
{
handleAjaxRequest(filterContext);
base.HandleUnauthorizedRequest(filterContext);
}
}
private static void handleAjaxRequest(AuthorizationContext filterContext)
{
var ctx = filterContext.HttpContext;
if (!ctx.Request.IsAjaxRequest())
return;
ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
ctx.Response.End();
}
}
}
در فیلتر فوق بررسی handleAjaxRequest اضافه شده است. در اینجا درخواستهای اعتبار سنجی نشده از نوع Ajax ایی خاتمه داده شده و سپس StatusCode ممنوع (403) به کلاینت بازگشت داده میشود. در این حالت کلاینت تنها کافی است StatusCode یاده شده را مدیریت کند:
using System.Web.Mvc;
using MvcApplication28.Helpers;
namespace MvcApplication28.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[SiteAuthorize]
[HttpPost]
public ActionResult SaveData(string data)
{
if(string.IsNullOrWhiteSpace(data))
return Content("NOk!");
return Content("Ok!");
}
}
}
در کد فوق نحوه استفاده از فیلتر جدید SiteAuthorize را ملاحظه میکنید. View ارسال کننده اطلاعات به اکشن متد SaveData، در ادامه بررسی میشود:
@{
ViewBag.Title = "Index";
var postUrl = this.Url.Action(actionName: "SaveData", controllerName: "Home");
}
<h2>
Index</h2>
@using (Html.BeginForm(actionName: "SaveData", controllerName: "Home",
method: FormMethod.Post, htmlAttributes: new { id = "form1" }))
{
@Html.TextBox(name: "data")
<br />
<span id="btnSave">Save Data</span>
}
@section Scripts
{
<script type="text/javascript">
$(document).ready(function () {
$("#btnSave").click(function (event) {
$.ajax({
type: "POST",
url: "@postUrl",
data: $("#form1").serialize(),
// controller is returning a simple text, not json
complete: function (xhr, status) {
var data = xhr.responseText;
if (xhr.status == 403) {
window.location = "/login";
}
}
});
});
});
</script>
}
تنها نکته جدید کدهای فوق، بررسی xhr.status == 403 است. اگر فیلتر SiteAuthorize کد وضعیت 403 را بازگشت دهد، به کمک مقدار دهی window.location، مرورگر را وادار خواهیم کرد تا صفحه کنترلر login را نمایش دهد. این کد جاوا اسکریپتی، با تمام مرورگرها سازگار است.
نکته تکمیلی:
در متد handleAjaxRequest، میتوان یک JavaScriptResult را نیز بازگشت داد تا همان کدهای مرتبط با window.location را به صورت خودکار به صفحه تزریق کند:
filterContext.Result = new JavaScriptResult { Script="window.location = '" + redirectToUrl + "'"};
البته این روش بسته به نحوه استفاده از jQuery Ajax ممکن است نتایج دلخواهی را حاصل نکند. برای مثال اگر قسمتی از صفحه جاری را پس از دریافت نتایج Ajax ایی از سرور، تغییر میدهید، صفحه لاگین در همین قسمت در بین کدهای صفحه درج خواهد شد. اما روش یاد شده در مثال فوق در تمام حالتها کار میکند.