در کدها و افزونهای که در ادامه ارائه خواهند شد، این مسایل درنظر گرفته شده است:
- چگونه اعتبار سنجی سمت کاربر را در حین استفاده از Ajax فعال کنیم.
- چگونه از چندبار کلیک کاربر در حین ارسال فرم به سرور جلوگیری نمائیم.
- چگونه Complex Types قابل تعریف در EF Code first را نیز در اینجا مدیریت کنیم.
- نحوه تعریف صحیح آدرسهای کنترلرها چگونه باید باشد.
- نحوه اعلام وضعیت لاگین شخص به او، در صورت بروز مشکل.
- ارسال صحیح anti forgery token در حین اعمال Ajax ایی.
- بررسی Ajax بودن درخواست رسیده و تهیه یک فیلتر سفارشی مخصوص آن.
- از کش شدن اطلاعات Ajax ایی جلوگیری شود.
ابتدا معرفی مدل برنامه
using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace jQueryMvcSample01.Models { public class User { [Required(ErrorMessage = "(*)"), DisplayName("نام")] public string Name { set; get; } public PhoneInfo PhoneInfo { set; get; } } public class PhoneInfo { [Required(ErrorMessage = "(*)"), DisplayName("تلفن")] public string Phone { get; set; } [Required(ErrorMessage = "(*)"), DisplayName("پیش شماره")] public string Ext { get; set; } } }
کدهای کنترلر برنامه
using System.Web.Mvc; using jQueryMvcSample01.Models; using jQueryMvcSample01.Security; namespace jQueryMvcSample01.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); //نمایش فرم } [HttpPost] [AjaxOnly] //فقط در حالت ایجکس قابل دسترسی باشد [ValidateAntiForgeryToken] public ActionResult Index(User user) { if (this.ModelState.IsValid) { // ذخیره سازی در بانک اطلاعاتی ... System.Threading.Thread.Sleep(3000); return Content("ok");//اعلام موفقیت آمیز بودن کار } return Content(null);//ارسال خطا } } }
چند نکته در اینجا حائز اهمیت هستند:
الف) استفاده از ویژگی AjaxOnly (که کدهای آنرا در پروژه پیوست میتوانید مشاهده نمائید)، جهت صرفا پردازش درخواستهای Ajaxایی.
ب) استفاده از ویژگی ValidateAntiForgeryToken در حین اعمال اجکسی. اگر سایتهای مختلف را در اینباره جستجو کنید، عموما برای پردازش آن در حین استفاده از jQuery Ajax بسیار مشکل دارند.
ج) استفاده از return Content برای اعلام نتیجه کار. اگر اطلاعات ثبت شد، یک ok یا هر عبارت دیگری که علاقمند بودید ارسال گردیده و در غیراینصورت null بازگشت داده میشود.
کدهای افزونه PostMvcFormAjax
// <![CDATA[ (function ($) { $.fn.PostMvcFormAjax = function (options) { var defaults = { postUrl: '/', loginUrl: '/login', beforePostHandler: null, completeHandler: null, errorHandler: null }; var options = $.extend(defaults, options); var validateForm = function (form) { //فعال سازی دستی اعتبار سنجی جیکوئری var val = form.validate(); val.form(); return val.valid(); }; return this.each(function () { var form = $(this); //اگر فرم اعتبار سنجی نشده، اطلاعات آن ارسال نشود if (!validateForm(form)) return; //در اینجا میتوان مثلا دکمهای را غیرفعال کرد if (options.beforePostHandler) options.beforePostHandler(this); //اطلاعات نباید کش شوند $.ajaxSetup({ cache: false }); $.ajax({ type: "POST", url: options.postUrl, data: form.serialize(), //تمام فیلدهای فرم منجمله آنتی فرجری توکن آنرا ارسال میکند complete: function (xhr, status) { var data = xhr.responseText; if (xhr.status == 403) { window.location = options.loginUrl; //در حالت لاگین نبودن شخص اجرا میشود } else if (status === 'error' || !data) { if (options.errorHandler) options.errorHandler(this); } else { if (options.completeHandler) options.completeHandler(this); } } }); }); }; })(jQuery); // ]]>
الف) فعال سازی دستی اعتبار سنجی جیکوئری، از این جهت که این نوع اعتبار سنجی به صورت پیش فرض تنها در حالت postback و ارسال کامل صفحه به سرور فعال میشود.
ب) استفاده از متد serialize جهت پردازش یکباره کل اطلاعات و فیلدهای یک فرم.
نکته مهم این متد ارسال فیلد مخفی anti forgery token نیز میباشد. فقط باید دقت داشت که این فیلد در حالتی که dataType به json تنظیم شود و همچنین از متد serialize استفاده گردد، در ASP.NET MVC پردازش نمیگردد (خیلی مهم!). به همین جهت در اینجا dataType تنظیمات jQuery Ajax حذف شده است.
ج) تنظیم cache به false در تنظیمات ابتدایی jQuery Ajax تا اطلاعات ارسالی و دریافتی کش نشوند و مشکل ساز نگردند.
د) بررسی xhr.status == 403 که توسط SiteAuthorizeAttribute (جایگزین بهتر فیلتر Authorize توکار ASP.NET MVC که کدهای آن در پروژه پیوست قابل دریافت است) و هدایت کاربر به صفحه لاگین
تعریف View ایی که از اشیاء تو در تو استفاده میکند و همچنین از افزونه فوق برای ارسال اطلاعات بهره خواهد برد:
@model jQueryMvcSample01.Models.User @{ ViewBag.Title = "تعریف کاربر"; var postUrl = Url.Action(actionName: "Index", controllerName: "Home"); } @using (Html.BeginForm(actionName: "Index", controllerName: "Home", method: FormMethod.Post, htmlAttributes: new { id = "UserForm" })) { @Html.ValidationSummary(true) @Html.AntiForgeryToken() <fieldset> <legend>تعریف کاربر</legend> <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.PhoneInfo.Ext) </div> <div class="editor-field"> @Html.EditorFor(model => model.PhoneInfo.Ext) @Html.ValidationMessageFor(model => model.PhoneInfo.Ext) </div> <div class="editor-label"> @Html.LabelFor(model => model.PhoneInfo.Phone) </div> <div class="editor-field"> @Html.EditorFor(model => model.PhoneInfo.Phone) @Html.ValidationMessageFor(model => model.PhoneInfo.Phone) </div> <p> <input type="submit" id="btnSave" value="ارسال" /> </p> </fieldset> } @section JavaScript { <script type="text/javascript"> $(document).ready(function () { $("#btnSave").click(function (event) { //جلوگیری از پست بک به سرور event.preventDefault(); var button = $(this); $("#UserForm").PostMvcFormAjax({ postUrl: '@postUrl', loginUrl: '/login', beforePostHandler: function () { //غیرفعال سازی دکمه ارسال button.attr('disabled', 'disabled'); button.val("..."); }, completeHandler: function () { //فعال سازی مجدد دکمه ارسال alert('انجام شد'); button.removeAttr('disabled'); button.val("ارسال"); }, errorHandler: function () { alert('خطایی رخ داده است'); } }); }); }); </script> }
@Html.EditorFor(model => model.PhoneInfo.Phone)
در ادامه نحوه استفاده از افزونه PostMvcFormAjax را مشاهده میکنید. چند نکته نیز در اینجا حائز اهمیت هستند:
الف) توسط htmlAttributes یک id برای فرم تعریف کردهایم تا در افزونه PostMvcFormAjax مورد استفاده قرار گیرد.
ب) postUrl و loginUrl را همانند متغیر تعریف شده در ابتدای View توسط Url.Action باید تعریف کرد تا در صورتیکه سایت ما در ریشه اصلی قرار نداشت، باز هم به صورت خودکار مسیر صحیحی محاسبه و ارائه گردد.
ج) نحوه غیرفعال سازی و فعال سازی دکمه submit را در روالهای beforePostHandler و completeHandler ملاحظه میکنید. این مساله برای جلوگیری از کلیکهای مجدد یک کاربر ناشکیبا و جلوگیری از ثبت اطلاعات تکراری بسیار مهم است.
د) کل این اطلاعات، در یک section به نام JavaScript ثبت شده است. این section در فایل layout برنامه به صورت زیر مورد استفاده قرار خواهد گرفت و به این ترتیب مقدار دهی خواهد شد:
<head> <title>@ViewBag.Title</title> <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.PostMvcFormAjax.js")" type="text/javascript"></script> @RenderSection("JavaScript", required: false) </head>
دریافت کدهای کامل این قسمت
jQueryMvcSample01.zip