$.get('http://site-url', function(data) { //این تابع پس از پایان کار عملیات ایجکسی در آینده فراخوانی خواهد شد });
$.get('http://site-url/0', function(data0) { // callback #1 $.get('http://site-url/1', function(data1) { // callback #2 $.post('http://site-url/2', function(data2) { // callback #3 }); }); });
روشهای زیادی برای حل این مساله ارائه شدهاست و در حال حاضر کار کردن با promiseها متداولترین روش حل مدیریت فراخوانی کدهای همزمان جاوا اسکریپتی است. برای نمونه اگر از AngularJS استفاده کنید، سرویسهای آن برای دریافت اطلاعات از سرور، از یک چنین مفهومی استفاده میکنند.
Promise در جاوا اسکریپت چیست؟
شیء Promise، نمایانگر قراردادی است که در آینده میتواند مورد قبول واقع شود، یا رد گردد. بررسی این قرارداد، تنها یکبار میتواند رخ دهد (پذیرش یا رد آن). هنگامیکه این بررسی صورت گرفت (رد یا پذیرش آن و نه هردو)، یک callback برای اطلاع رسانی فراخوانی میگردد. سپس این callback میتواند یک Promise دیگر را سبب شود. به این ترتیب میتوان Promiseها را زنجیر وار به یکدیگر متصل کرد. برای نمونه jQuery به صورت توکار از promises پشتیبانی میکند:
// returns a promise $.get('http://site-url/0') .then(function(data) { // callback 1 // returns a promise return $.get('http://site-url/1'); }) .then(function(data) { // callback 2 // returns a promise return $.post('http://site-url/2'); }) .then(function(data) { // callback 3 });
در این حالت، هر callback حداقل سه کار را میتواند انجام دهد:
الف) یک promise دیگر را بازگشت دهد. نمونه آنرا با return $.get در کدهای فوق ملاحظه میکنید.
ب) خاتمه عادی. همینجا کار promise با مقدار بازگشت داده شده، پایان مییابد.
ج) صدور یک استثناء. سبب برگشت خوردن و عدم پذیرش promise میشود.
استفاده از Promises در سایر کتابخانهها
jQuery پیاده سازی توکاری از promises دارد؛ اما سایر کتابخانهها، مانند AngularJS ایی که مثال زده شده چطور عمل میکنند؟
استانداردی به نام +Promises/A جهت یک دست سازی پیاده سازیهای promise در جاوا اسکریپت پیشنهاد شدهاست. jQuery نیمی از آنرا پیاده سازی کردهاست؛ اما کتابخانهی دیگری به نام Q Library، پیاده سازی نسبتا مفصلتری را از این استاندارد ارائه میدهد. فریم ورک AngularJS نیز در پشت صحنه از همین کتابخانه برای پیاده سازی promises استفاده میکند.
آشنایی با کتابخانه Q
استفاده مقدماتی از Q همانند مثالی است که از jQuery ملاحظه کردید.
Q.fcall(callback1) .then(callback2);
Q.fcall(function() { return $.get('http://my-url'); }) .then(callback3);
function waitForClick() { var deferred = Q.defer(); $('#okButton').click(function() { deferred.resolve(); }); $('#cancelButton').click(function() { deferred.reject(); }); return deferred.promise; } Q.fcall(waitForClick) .then(function() { // ok button was clicked }, function() { // cancel button was clicked });
در ادامه کار، اینبار متد then، دو callback را قبول میکند. Callback اول پس از پذیرش قرار داد و Callback دوم پس از رد قرار داد، فراخوانی خواهد گردید.
در رنجیره تعریف شده، اگر معادلی برای reject درنظر گرفته نشده باشد، مانند مثال ذیل:
Q.fcall(myFunction1) .then(success1) .then(success2, failure1);
همچنین اگر نتیجهی success1 با شکست مواجه شود نیز failure1 فراخوانی میگردد. اما باید درنظر داشت که شکست success2، توسط failure1 مدیریت نمیشود.
Promises در AngularJS
در AngularJS امکانات کتابخانه Q توسط پارامتری به نام q$ در اختیار سرویسهای برنامه قرار میگیرد (تزریق میشود):
var app = angular.module("myApp", []); app.factory('dataSvc', function($http, $q){ var basePath="api/books"; getAllBooks = function(){ var deferred = $q.defer(); $http.get(basePath).success(function(data){ deferred.resolve(data); }).error(function(err){ deferred.reject("service failed!"); }); return deferred.promise; }; return{ getAllBooks:getAllBooks }; }); app.controller('HomeController', function($scope, $window, dataSvc){ function initialize(){ dataSvc.getAllBooks().then(function(data){ $scope.books = data; }, function(msg){ $window.alert(msg); }); } initialize(); });
اکنون در کنترلری که قرار است از این سرویس استفاده کند، متد then کتابخانه Q را ملاحظه میکنید که دو Callback متناظر resolve و reject مدیریت promise بازگشت داده شده را به همراه دارد. اگر عملیات Ajaxایی موفقیت آمیز باشد، شیء books را مقدار دهی میکند و اگر خیر، پیامی را به کاربر نمایش خواهد داد.
پشتیبانی مرورگرهای جدید از استاندارد Promise
در حال حاضر کروم 32 و نگارشهای شبانه فایرفاکس، Promise را که جزئی از استاندارد JavaScript شدهاست، به صورت توکار و بدون نیاز به کتابخانههای جانبی، پشتیبانی میکنند.
if (window.Promise) { // Check if the browser supports Promises var promise = new Promise(function(resolve, reject) { //asynchronous code goes here }); }
if (window.Promise) { console.log('Promise found'); var promise = new Promise(function(resolve, reject) { // async if (result) { resolve(data); } else { reject('error'); } }); promise.then(function(data) { console.log('Promise fulfilled.'); }, function(error) { console.log('Promise rejected.'); }); } else { console.log('Promise not available'); }
بر اساس آموزش مدیریت حالت در Blazor، قصد داریم یک سرویس پیام هشدار ساده، ولی زیبا را بوسیله کامپوننت Alert بوت استرپ ۵ ، بدون استفاده از توابع جاوا اسکریپتی، طراحی کنیم.
در ابتدا کتابخانههای css زیر را بوسیله LibMan به پروژه اضافه کرده و مداخل فایلهای را css نیز اضافه میکنیم:
{ "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "unpkg", "library": "bootstrap@5.0.0", "destination": "wwwroot/lib/bootstrap" }, { "provider": "unpkg", "library": "open-iconic@1.1.1", "destination": "wwwroot/lib/open-iconic" }, { "provider": "unpkg", "library": "animate.css@4.1.1", "destination": "wwwroot/lib/animate" }, { "provider": "unpkg", "library": "bootstrap-icons@1.5.0", "destination": "wwwroot/lib/bootstrap-icons/" } ] }
public enum AlertType { Success, Info, Danger, Warning } public class AlertService { public void ShowAlert(string message, AlertType alertType, string animate = "animate__fadeIn") { OnChange?.Invoke(message, alertType,animate); } public event Action<string,AlertType, string> OnChange; }
services.AddScoped<AlertService>();
توضیحات:
در کدهای نهایی برنامه قرار است به این نحو کار نمایش Alertها را در کامپوننتهای مختلف انجام دهیم:
@inject AlertService AlertService @code { private void Success() { AlertService.ShowAlert("Success!", AlertType.Success); }
کدهای کامپوننت Alert.razor
@inject AlertService AlertService @implements IDisposable <style> .alert-show { display: flex; flex-direction: row; } .alert-hide { display: none; } </style> <div style="z-index: 5"> <div " + "alert-show" :"alert-hide")"> <i width="24" height="24"></i> <div> @Message </div> <button type="button" data-bs-dismiss="alert" aria-label="Close" @onclick="CloseClick"></button> </div> </div> @code { AlertType AlertType { get; set; } string Icon { get; set; } string Css { get; set; } string Animation { get; set; } private bool IsVisible { get; set; } private string Message { get; set; } System.Timers.Timer _alertTimeOutTimer; protected override void OnInitialized() { AlertService.OnChange += ShowAlert; } private void ShowAlert(string message, AlertType alertType, string animate) { _alertTimeOutTimer = new System.Timers.Timer { Interval = 5000, Enabled = true, AutoReset = false }; _alertTimeOutTimer.Elapsed += HideAlert; Message = message; switch (alertType) { case AlertType.Success: Css = "bg-success"; Icon = "bi-check-circle"; break; case AlertType.Info: Css = "bg-info"; Icon = "bi-info-circle-fill"; break; case AlertType.Danger: Css = "bg-danger"; Icon = "bi-exclamation-circle"; break; case AlertType.Warning: Css = "bg-warning"; Icon = "bi-exclamation-triangle-fill"; break; default: Css = Css; break; } AlertType = alertType; Animation = animate; IsVisible = true; InvokeAsync(StateHasChanged); } private void HideAlert(Object source, System.Timers.ElapsedEventArgs e) { IsVisible = false; InvokeAsync(StateHasChanged); _alertTimeOutTimer.Close(); } public void Dispose() { if (AlertService != null) AlertService.OnChange -= ShowAlert; if (_alertTimeOutTimer != null) { _alertTimeOutTimer.Elapsed -= HideAlert; _alertTimeOutTimer?.Dispose(); } } private void CloseClick() { IsVisible = false; _alertTimeOutTimer.Close(); InvokeAsync(StateHasChanged); } }
<div> <Alert></Alert>
مشکل:
دلیل:
راه حل:
function enableBootstrapStyleValidation() { $.validator.setDefaults({ ignore: "", highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.form-group').removeClass('has-success').addClass('has-error'); } $(element).trigger('highlited'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.form-group').removeClass('has-error').addClass('has-success'); } $(element).trigger('unhighlited'); } }); }
$("#form").validate({ ... rules: {...}, messages: {...}, ignore: "", ... });
پایان پروژه ASP.NET Ajax Control Toolkit !
هدف مایکروسافت از اینکار مدیریت هر دو پروژه MVC و Web forms است آن هم با هزینه کم، کیفیت بالا و سازگار با تمام مرورگرها.
ضمنا این رو در نظر باشید که یکی از توانمندیهای jQuery، Ajax است (از کار با DOM گرفته تا دستکاری CSS نمایش داده شده، تا Animation ، مدیریت سادهتر رخدادها، اعمال قالب به سایت و غیره) و این مورد مزیت مهمی است نسبت به تمام کتابخانههایی که فقط برای یک کار و آن هم سهولت تولید برنامههای مبتنی بر Ajax ایجاد شدهاند و از چند مشکل مهم رنج میبرند:
- تک کارهاند. فقط Ajax .
- مشکل سازگاری با مرورگرهای مختلف را دارند.
- به صورت فعال توسعه داده نمیشوند؛ رفع باگ نمیشوند و غیره. برای مثال فواصل به روز رسانی همان Anthem.Net را بررسی کنید.
- توسعه پذیر نیستند. برای مثال آیا میتوان برای Anthem.Net افزونه نوشت؟
- حجم بالایی دارند.
- سرعت پایینی دارند.
jQuery در مورد تمام موارد عنوان شده حرف برای گفتن دارد از حجم کم تا سرعت بالاتر نسبت به اکثر کتابخانههای جاوا اسکریپتی دیگر تا توسعهی منظم، سازگاری عالی با مرورگرها، توسعه پذیری و صدها و هزاران افزونهی مهیا برای آن و غیره.
و RAD هزینه بر است. یعنی چی؟ یعنی حجم بالای کدهای اسکریپتی که باید به برنامهی شما مثلا توسط ASP.NET Ajax تزریق شود که مبادا شما بخواهید دست خودتان را به نوشتن چند سطر کد جاوا اسکریپتی آلوده کنید. همچنین حجم تبادل اطلاعات ASP.NET Ajax را هم که مبتنی است بر RAD را هم با حجم اطلاعات مبادله شده توسط jQuery مقایسه کنید (با پلاگین فایرباگ مربوط به فایرفاکس). این حجم واقعا زیاد است و قابل مقایسه نیست. (تمام اینها هزینههای RAD است)
ضمنا وجود 100 ها افزونه و پلاگین نوشته شده برای jQuery کار شما را بسیار بسیار ساده میکنند، مانند پاسخ قبلی من در این مطلب.
فقط باید کمی وقت بگذارید و چیزی را بیاموزید که واقعا ارزش دارد. گیرم فردا نخواستید با ASP.NET کار کنید. تمام این اطلاعات در PHP هم به درد شما میخورد، چون قسمت سمت کلاینت آنچنان تفاوتی نمیکند و jQuery یک کتابخانهی سمت کلاینت است.
آیا از پلاگین خاصی باید استفاده کنیم یا باید بصورت دستی این امکان رو اضافه کنیم؟
AngularJS #4
html
<div ng-app="myApp" id="ng-app"> <div ng-controller="MenuCtrl" style="width:300px"> <div style="height:200px;overflow:auto;"> <div ng-repeat="menu in menu" > <div style="float:right;cursor:pointer;" ng-click="remove(menu.ID,$index);">X</div> <a href="#"> <img style="width:32px;" ng-src="/Content/user.gif" alt="{{menu.Title}}"> </a> <div> <h4>{{menu.Title}}</h4> {{menu.Url}} </div> </div> </div> <form action="/Menu/Add" method="post"> <div> <label for="Title">عنوان</label> <input id="Title" type="text" name="Title" ng-model="menu.Title" placeholder="عنوان" /> </div> <div> <label for="Url">آدرس</label> <input id="Url" type="text" name="Url" ng-model="menu.Url" placeholder="آدرس" /> </div> <div> <label for="ParentID">والد</label> <input id="ParentID" type="text" name="ParentID" ng-model="menu.ParentID" placeholder="والد" /> </div> <button type="button" ng-click="addmenu()">ذخیره</button> </form> </div> </div>
var app = angular.module('myApp', ['ngAnimate']); app.controller('MenuCtrl', function ($scope, $http) { $scope.menu = {}; $http.get('/Menu/GetAll').success(function (data) { $scope.menu = data; }) $scope.addmenu= function () { $http.post("/Menu/Add", $scope.menu).success(function () { $scope.menus.push({ Title: $scope.menu.Title, Url: $scope.menu.Url, ParentID: $scope.menu.ParentID }); $scope.menu = {}; }); }; $scope.remove = function (ID, index) { $http.post("/Menu/Remove", { ID: ID }).success(function () { $scope.menu.splice(index, 1); }); }; });
public class MenuController : Controller { // // GET: /Menu/ MyContext _db = new MyContext(); public ActionResult GetAll() { var menu = _db.Menus.ToList(); var result = JsonConvert.SerializeObject(menu, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }); return Content(result); } public ActionResult Add(Menu menu) { _db.Menus.Add(menu); _db.SaveChanges(); return Json("1"); } public ActionResult Remove(int id) { var selectedMenu = new Menu { ID = id }; _db.Menus.Attach(selectedMenu); _db.Menus.Remove(selectedMenu); _db.SaveChanges(); return Json("1"); } public ActionResult Index() { return View(); } }
اصول نمایش Tooltip در Bootstrap
Tooltip یکی دیگر از کامپوننتهای جاوا اسکریپتی Bootstrap است.
<a rel="tooltip" title="یک سری توضیحات در اینجا" href="#">اطلاعات</a> <script type="text/javascript"> $(document).ready(function () { $("[rel='tooltip']").tooltip({placement:'top', trigger : 'hover'}); }); </script>
برای المان مورد نظر، یک title تعریف کردهایم. برای اینکه tooltip پیش فرض مرورگر در این حالت ظاهر نشود، یک rel=tooltip را در اینجا به المان اضافه کرده و سپس افزونه tooltip بوت استرپ، بر روی کلیه المانهایی با rel=tooltip فراخوانی شده است.
placement، محل قرارگیری tooltip را مشخص میکند؛ مانند بالا، پایین، چپ و راست. trigger مشخص میکند که این tooltip در چه زمانی ظاهر شود. برای مثال در حالت hover یا در حالت focus یک المان.
در اینجا بجای title، از ویژگی data-original-title نیز میتوان استفاده کرد.
تبدیل خطاهای اعتبارسنجی ASP.NET MVC به Tooltip
هدف ما در اینجا، نهایتا رسیدن به شکل زیر میباشد:
همانطور که ملاحظه میکنید، اینبار بجای نمایش خطاها در یک برچسب مقابل کنترل متناظر، این خطا صرفا در حالت فوکوس کنترل، به شکل یک Tooltip در بالای آن ظاهر شده است.
کدهای کامل View برنامه
@model Mvc4TwitterBootStrapTest.Models.User @{ ViewBag.Title = "تعریف کاربر جدید"; } @using (Html.BeginForm()) { @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" }) <fieldset class="form-horizontal"> <legend>تعریف کاربر جدید</legend> <div class="control-group"> @Html.LabelFor(model => model.Name, new { @class = "control-label" }) <div class="controls"> @Html.EditorFor(model => model.Name) @*@Html.ValidationMessageFor(model => model.Name, null, new { @class = "help-inline" })*@ </div> </div> <div class="control-group"> @Html.LabelFor(model => model.LastName, new { @class = "control-label" }) <div class="controls"> @Html.EditorFor(model => model.LastName) @*@Html.ValidationMessageFor(model => model.LastName, null, new { @class = "help-inline" })*@ </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary"> ارسال</button> </div> </fieldset> } @section JavaScript { <script type="text/javascript"> $.validator.setDefaults({ showErrors: function (errorMap, errorList) { this.defaultShowErrors(); //اگر المانی معتبر است نیاز به نمایش تولتیپ ندارد $("." + this.settings.validClass).tooltip("destroy"); //افزودن تولتیپها for (var i = 0; i < errorList.length; i++) { var error = errorList[i]; $(error.element).tooltip({ trigger: "focus" }) // فقط در حالت فوکوس نمایش داده شود .attr("data-original-title", error.message); } }, // همانند قبل برای رنگی کردن کل ردیف در صورت عدم اعتبار سنجی و برعکس highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.control-group').removeClass('success').addClass('error'); } $(element).trigger('highlited'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.control-group').removeClass('error').addClass('success'); } $(element).trigger('unhighlited'); } }); //برای حالت پست بک از سرور عمل میکند $(function () { $('form').each(function () { $(this).find('div.control-group').each(function () { if ($(this).find('span.field-validation-error').length > 0) { $(this).addClass('error'); } }); }); }); </script> }
توضیحات
- با توجه به اینکه دیگر نمیخواهیم خطاها به صورت برچسب در مقابل کنترلها نمایش داده شوند، کلیه Html.ValidationMessageFor به صورت کامنت درآورده شدهاند.
- تغییر دوم مطلب جاری، اضافه شدن متد showErrors به تنظیمات پیش فرض jQuery Validator است. در این متد، اگر المانی معتبر بود، Tooltip آن حذف میشود؛ یا در سایر حالات، المانهایی که نیاز به اعتبارسنجی سمت کلاینت دارند، یافت شده و سپس ویژگی data-original-title با مقداری معادل خطای اعتبارسنجی متناظر، به این المان افزوده شده و سپس متد tooltip بوت استرپ بر روی آن فراخوانی میگردد.
به عبارتی زمانیکه یک input box در ASP.NET MVC به همراه مقادیر مرتبط با اعتبارسنجی آن رندر میشود، چنین شکلی را خواهد داشت:
<input class="text-box single-line" data-val="true" data-val-required="لطفا نام را تکمیل کنید" id="Name" name="Name" type="text" value="" />
<input class="text-box single-line input-validation-error" data-val="true" data-val-required="لطفا نام را تکمیل کنید" id="Name" name="Name" type="text" value="" data-original-title="لطفا نام را تکمیل کنید" title="">
برای رفع این نقیصه با استفاده از قطعه کد زیر میتوان کلیه کوئری استرینگهای صفحه را یافت و به شیءایی به نام urlParams به صورت خاصیت اضافه کرد:
var urlParams; (window.onpopstate = function () { var match, pl = /\+/g, // Regex for replacing addition symbol with a space search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, query = window.location.search.substring(1); urlParams = {}; while (match = search.exec(query)) urlParams[decode(match[1])] = decode(match[2]); })();
$.ajax({ type: "POST", url: "@sortUrl", data: JSON.stringify({ items: items, surveyId: surveyId }),
function jsonConcat(defaults, options) { /* merge defaults and options, without modifying defaults */ return $.extend({}, defaults, options); }
var ajaxData = { items: items, surveyId: surveyId }; $.ajax({ type: "POST", url: "@sortUrl", data: JSON.stringify(jsonConcat(ajaxData,urlParams)),
هدایت خودکار کاربر به صفحه لاگین در حین اعمال Ajax ایی
Angular Interceptors
ابتدا مشکل و هدف را بیان میکنیم:
مشکل: کاربر در صفحهای حضور دارد که نیاز به اعتبارسنجی داشته و مدت اعتبار کاربر نیز تمام شده است، ولی هنوز در صفحهای که نباید حضور داشته باشد، حضور دارد و بدتر از آن این است که میتواند درخواستهای بی نتیجهای را نیز ارسال کند.
هدف: کاربر را سریعا به صفحهای که به آن تعلق دارد هدایت کنیم ( یعنی صفحهی ورود به سیستم ).
و حالا از ابتدا پروسه را دنبال میکنیم. یک Controller سمت سرور داریم به این صورت :
[Authorize(Roles = AuthorizeRole.SuperAdministrator)] public partial class HomeController : Controller { [HttpPost] [AngularValidateAntiForgeryToken] public virtual JsonResult GetUserInfo() { var userInfoViewModel = _applicationUserManager.GetUserInfoById(User.Identity.GetUserId()); return Json(userInfoViewModel); } }
یعنی با کدی همانند کد زیر:
$scope.getUserInfo = function() { $http({ method: 'POST', url: 'Home/GetUserInfo', headers: $scope.getHeaders() }). success(function(data, status, headers, config) { $scope.userInfo = data; }). error(function(data, status, headers, config) { }).then(function(res) { }); }
و نتیجهی کدهای بالا به صورت زیر درخواهد آمد :
همانطور که میبینید دادههای اولیه کاربر پس از ورود به سیستم، بدون هیچ مشکلی دریافت میشوند.
نکته : زمانیکه status برابر با 200 هست، یعنی درخواست OK میباشد. ( در پیوست ، لیست تمامی کدها قرار داده شده است )
حالا فرض کنید کاربر در صفحه حضور دارد و به هر دلیلی اعتبار حضور کاربر منقضی شده است و حالا پس از مدتی کاربر درخواستی را به سرور ارسال میکند و میخواهد اطلاعات خودش را مشاهده کند.
درخواست کاربر با همان کدهای اولیه ارسال میشود و خروجی اینبار به صورت زیر در خواهد آمد :
همانطور که میبینید وضعیت اینبار نیز OK میباشد، ولی هیچ دادهای از سرور دریافت نشده است. کاربر قطعا در اینجا دچار سردرگمی میشود. چون هیچ چیزی را مشاهده نمیکند و به هیچ مسیر دیگری نیز هدایت نمیشود و هرکار دیگری نیز انجام دهد، پاسخی مشاهده نمیکند.
راه حل چیست ؟
ابتدا باید برای درخواستهای Ajax ایی اعتبارسنجی را اعمال کنیم. برای این کار باید یک Attribute جدید بسازیم. یعنی به این صورت :
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class MyCustomAuthorize : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { // یعنی اعتبارسنجی نشده است filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; filterContext.HttpContext.Response.End(); } base.HandleUnauthorizedRequest(filterContext); } }
توجه : کد کاملتر همراه با توضیحات در بخش پیش نیازها آمده است.
حالا در سمت کلاینت نیز به این صورت عمل میکنیم :
$http({ method: 'POST', url: 'Home/GetUserInfo', headers: $scope.getHeaders() }). success(function(data, status, headers, config) { $scope.userInfo = data; }). error(function(data, status, headers, config) { if (status === 401) { // you are not authorized } }).then(function(res) { });
حالا دیگر متوجه خواهیم شد که کاربر اعتبارسنجی نشده است، یا اعتبار آن منقضی شده است و میتوانیم کاربر را به مسیر "ورود به سیستم" هدایت کنیم.
در console مرورگر نیز خطای زیر رخ میدهد :
POST http://localhost:000000/Administrator/Home/GetUserInfo 401 (Unauthorized)
حالا باید از یک Interceptor استفاده و درخواستهای HTTP خودمان را مدیریت کنیم. برای این منظور ما یک Interceptor جدید را همانند کدهای زیر مینویسیم:
factory('AuthorizedInterceptor', function ($q) { return { response: function(response) { return response || $q.when(response); }, responseError: function(rejection) { if (rejection.status === 401) { // you are not authorized } return $q.reject(rejection); } }; })
همانطور که مشاهده میکنید کدهای خطای درخواست http$ را در Interceptor قرار دادیم. حالا کاربر درخواستی را ارسال میکند و ما با توجه به این Interceptor متوجه خواهیم شد که آیا اعتبار آن منقضی شده است یا خیر و در صورت منقضی شدن، کاربر را به صفحهی ورود هدایت خواهیم کرد. یعنی بدین صورت :
در خروجی بالا میبینید که کاربر، اعتبارسنجی نشده است و آن را به مسیر ورود هدایت خواهیم کرد.
با Interceptor بالا دیگر نیازی نیست برای هر درخواستی این وضعیت را چک کنیم و به صورت خودکار این چک کردن رخ خواهد داد.
پیوست : http_status_codes.rar