Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
با تشکر
Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
RouteTable.Routes.MapPageRoute("Gallery", "{PageName In Database}", "~/Main.aspx");
[ServiceContract] public interface IService1 { [OperationContract] Order InsertOrder(); [OperationContract] void UpdateOrderWithoutRetrieving(Order order); [OperationContract] void UpdateOrderByRetrieving(Order order); }
public class Service1 : IService1 { public Order InsertOrder() { using (var context = new EFRecipesEntities()) { // remove previous test data context.Database.ExecuteSqlCommand("delete from [orders]"); var order = new Order { Product = "Camping Tent", Quantity = 3, Status = "Received" }; context.Orders.Add(order); context.SaveChanges(); return order; } } public void UpdateOrderWithoutRetrieving(Order order) { using (var context = new EFRecipesEntities()) { try { context.Orders.Attach(order); if (order.Status == "Received") { context.Entry(order).Property(x => x.Quantity).IsModified = true; context.SaveChanges(); } } catch (OptimisticConcurrencyException ex) { // Handle OptimisticConcurrencyException } } } public void UpdateOrderByRetrieving(Order order) { using (var context = new EFRecipesEntities()) { // fetch current entity from database var dbOrder = context.Orders .Single(o => o.OrderId == order.OrderId); if (dbOrder != null && // execute concurrency check StructuralComparisons.StructuralEqualityComparer.Equals(order.TimeStamp, dbOrder.TimeStamp)) { dbOrder.Quantity = order.Quantity; context.SaveChanges(); } else { // Add code to handle concurrency issue } } } }
class Program { static void Main(string[] args) { var service = new Service1Client(); var order = service.InsertOrder(); order.Quantity = 5; service.UpdateOrderWithoutRetrieving(order); order = service.InsertOrder(); order.Quantity = 3; service.UpdateOrderByRetrieving(order); } }
متد ()InsertOrder دادههای پیشین را حذف میکند، سفارش جدیدی میسازد و آن را در دیتابیس ثبت میکند. در آخر موجودیت جدید به کلاینت باز میگردد. موجودیت بازگشتی هر دو مقدار OrderId و TimeStamp را دارا است که توسط دیتابیس تولید شده اند. سپس در کلاینت از دو رویکرد نسبتا متفاوت برای بروز رسانی موجودیت استفاده میکنیم.
در رویکرد نخست، متد ()UpdateOrderWithoutRetrieving موجودیت دریافت شده از کلاینت را Attach میکند و چک میکند که مقدار فیلد Status چیست. اگر مقدار این فیلد "Received" باشد، فیلد Quantity را با EntityState.Modified علامت گذاری میکنیم و متد ()SaveChanges را فراخوانی میکنیم. EF دستورات لازم برای بروز رسانی را تولید میکند، که فیلد quantity را مقدار دهی کرده و یک عبارت where هم دارد که فیلدهای OrderId و TimeStamp را چک میکند. اگر مقدار TimeStamp توسط یک دستور بروز رسانی تغییر کرده باشد، بروز رسانی جاری با خطا مواجه خواهد شد. برای مدیریت این خطا ما بدنه کد را در یک بلاک try/catch قرار میدهیم، و استثنای OptimisticConcurrencyException را مهار میکنیم. این کار باعث میشود اطمینان داشته باشیم که موجودیت Order دریافت شده از متد ()InsertOrder تاکنون تغییری نکرده است. دقت کنید که در مثال جاری تمام خواص موجودیت بروز رسانی میشوند، صرفنظر از اینکه تغییر کرده باشند یا خیر.
رویکرد دوم نشان میدهد که چگونه میتوان وضعیت همزمانی موجودیت را پیش از بروز رسانی مشخصا دریافت و بررسی کرد. در اینجا میتوانید مقدار TimeStamp موجودیت را از دیتابیس بگیرید و آن را با مقدار موجودیت کلاینت مقایسه کنید تا وجود تغییرات مشخص شود. این رویکرد در متد ()UpdateOrderByRetrieving نمایش داده شده است. گرچه این رویکرد برای تشخیص تغییرات خواص موجودیتها و یا روابط شان مفید و کارآمد است، اما بهترین روش هم نیست. مثلا ممکن است از زمانی که موجودیت را از دیتابیس دریافت میکنید، تا زمانی که مقدار TimeStamp آن را مقایسه میکنید و نهایتا متد ()SaveChanges را صدا میزنید، موجودیت شما توسط کلاینت دیگری بروز رسانی شده باشد.
مسلما رویکرد دوم هزینه برتر از رویکرد اولی است، چرا که برای مقایسه مقادیر همزمانی موجودیت ها، یکبار موجودیت را از دیتابیس دریافت میکنید. اما این رویکرد در مواقعی که Object graphهای بزرگ یا پیچیده (complex) دارید بهتر است، چون پیش از ارسال موجودیتها به context در صورت برابر نبودن مقادیر همزمانی پروسس را لغو میکنید.
[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)
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); } }; })
در خروجی بالا میبینید که کاربر، اعتبارسنجی نشده است و آن را به مسیر ورود هدایت خواهیم کرد.
با Interceptor بالا دیگر نیازی نیست برای هر درخواستی این وضعیت را چک کنیم و به صورت خودکار این چک کردن رخ خواهد داد.
پیوست : http_status_codes.rar