جميعنا يعلم أهمية التزامن في البرمجيات فمعظم الـ API حالتها ضعيفة بعد جلبها من المصدر، فإذا كنت تريد ان تحكم سيطرتك على البيانات فبالتالي انت مطالب بعمل إضافي في برمجياتك.
نفترض ان لدينا مجموعة بيانات قام المستخدم A بجلبها من قاعدة البيانات، وبعدها بلحظات قام المستخدم B بجلب نفس البيانات لعمل تعديل عليها ماذا تتوقع أن يحدث في تلك الحالة إذا قام المستخدم B بتغيير البيانات. ماهي حالة المستخدم A هنا؟
من المؤكد أن المستخدم A في هذه الحالة سيقوم بالتعامل مع بيانات قديمة.
هذه المقالة تعبتر متقدمة نوعا ما Advanced وموجهة للمبرمجين المحترفين بصورة عامة، فإن كنت تواجه صعوبة كبيرة في المتابعة فبإمكانك تركها وعدم قراءتها فقط يكفيك أن تقوم بإنشاء حقل في كل جداول قاعدة البيانات خاصتك يأخد النوع rowversion وبالتالي سيقوم الـ SQL Server بالتحكم في جميع البيانات سواء التي تم جلبها والتي يريد المستخدم حفظها
حسنا إذا كنت مواصل معي في القراءة فمرحبا بك.
ماهو الـ ETag
هو عبارة عن معرف (في الغالب GUID) يتم تخزينه بصورة مؤقتة مع جميع الـ Requests والـ Responses فيساعد في سرعة التقليل من الـ BandWidth بحيث أنه في حال قمت بطلب جلب بيانات لم تتغير منذ الـ Request الأول بالتالي سيتم عرض نفس البيانات بدون توليدها مرة أخرى، أيضا يساعد في عدم حدوث overwrite للبيانات كما شرحنا سابقا في حالتي المستخدمين A و B.
قم بإنشاء مشروع ASP.NET Core جديد وقم بإنشاء Class بإسم داخل المشروع بإسم HashFactory وقم بإنشاء Method داخل نفس الـ Class بإسم GetHashFromProprites الشفرة كاملة لهذا الـ Class ستكون كتالي:
public static class HashFactory
{
public static string GetHashFormProperties(Type type)
{
if (type == null)
{
return string.Empty;
}
string propertyToHash = string.Empty;
var getProperites = from m in type.GetProperties() select m.Name.ToString(); // Use Reflection
foreach (var data in getProperites)
{
propertyToHash = data.ToList().ToString();
}
using var md5 = MD5.Create();
byte[] dataRow = md5.ComputeHash(Encoding.UTF8.GetBytes(propertyToHash));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dataRow.Length; i++)
{
sb.Append(dataRow[i].ToString("x2"));
}
return sb.ToString();
}
مهمة هذا الـ Class هي ان يقوم بتوليد رقم من النوع Guid وهي الرقم الذي يتم إسناده للـ ETag header وقمت بإستخدام الخوازمية MD5 وبها Method تسمى CreateHash، ومحتوى الـ Guid الذي يتم توليده عبارة عن جميع الـ Property للـ Class الذي سيتم عليه عمليتا الـ Request والـ Response وقد قمت بإستخدام الـ Reflection لجلب جميع الـ Properties للـ Object الذي نريد التعامل معه وقمت بإستخدام تقنية Linq لإختيار اسم الـ Property فقط.
قم بإنشاء Controller وبها اسلوبين الأول عبارة عن GET والآخر عبارة عن PUT الشفرة ستكون كالتالي، سأقوم بالشرح بعد تنفيذ المشروع:
const string ETAG_HEADER = "ETag";
const string MATCH_HEADER = "If-Match";
[HttpGet("{id}",Name ="GetPostById")]
public IActionResult GetById(int id)
{
var post = _context.Posts.Where(x => x.Id == id).FirstOrDefault();
var eTag = HashFactory.GetHashFormProperties(typeof(Post));
HttpContext.Response.Headers.Add(ETAG_HEADER, eTag);
if(HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) &&
HttpContext.Request.Headers[MATCH_HEADER].Contains(eTag))
{
return new StatusCodeResult(StatusCodes.Status304NotModified);
}
return new ObjectResult(post);
}
[HttpPut("{id}",Name ="UpdatePostById")]
public async Task<IActionResult> Update(int id, [FromBody] Post post)
{
if(post == null || post.Id != id)
{
return BadRequest();
}
var postToUpdate = _context.Posts.FirstOrDefault(x => x.Id == id);
if(postToUpdate != null)
{
return NotFound();
}
var contextTag = HashFactory.GetHashFormProperties(typeof(Post));
if(!HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) ||
!HttpContext.Request.Headers[MATCH_HEADER].Contains(contextTag))
{
return new StatusCodeResult(StatusCodes.Status412PreconditionFailed);
}
postToUpdate.Name = post.Name;
_context.Posts.Update(post);
await _context.SaveChangesAsync();
return new NoContentResult();
}
حسنا هنا في الحدث GET قمت بإضافة الـ ETag header مع الـ Request وقبل جلب البيانات .
بعدها قمت بالتأكد في حال كان الـ Header قام بإرسال الـ Etag من خلال If-Match header والذي سيقوم بالتأكد من أن المعرف GUID الذي تم إرساله مع الـ Request الأول هو نفسه، هنا لدينا حالتين
الأولى: إذا كان تم إرساله من قبل فيسقوم الـ API بإرجاع 304NotModified Status فبالتالي لن يقوم السيرفر بمخاطبة قاعدة البيانات او XML او أي Provider لجلب البيانات بل سيقوم بجلبها من البيانات المخزنة لديه من Chache
الحالة الثانية: إذا أراد مستخدم آخر أن يعدل في البيانات لابد أن يقوم بإرسال الـ If-Match header مع الـ Request وذلك حتى لايحدث تضارب في جلب البيانات بعد تعديلها بالنسبة للمستخدم الأول الذي قام بعمل Request.



بإمكانك التجربة من خلال الـ Postman ورؤية ما قمنا بشرحه بصورة عملية.
في الحدث PUT قمت بالتأكد من أنه لابد أن يقوم المستخدم بإرسال If-Match header وذلك حتى يتسنى لي مقارنة الـ Guid الذي تم تخزينه مع الـ Request مع نفس الـ Guid الذي سيتم إرساله في هذا الحدث PUT فإذا كانا مختلفين فحينها ستم إجهاض عملية التعديل وسيتم إرجاع 412PreConditionFailed status
تحياتي
order enclomiphene canada on sale
how to order enclomiphene usa drugstore
generique kamagra prescrire sun medicament pharmacie
meilleurs prix kamagra en ligne
get androxal without prescriptions canada
buy cheap androxal cheap europe
purchase flexeril cyclobenzaprine cheap canada pharmacy
buying flexeril cyclobenzaprine cheap from canada
purchase dutasteride canada cost
order dutasteride generic from canadian pharmacy
get gabapentin generic online usa
buy gabapentin uk london
buying fildena singapore where to buy
cheapest buy fildena uk delivery
purchase itraconazole generic available in united states
ordering itraconazole purchase online canada
how to order staxyn generic europe
how to order staxyn price generic
order avodart generic overnight delivery
purchase avodart buy safely online
discount xifaxan usa discount
how to order xifaxan real price
Buy rifaximin online with overnight delivery
get rifaximin cost australia
objednejte si kamagra online od fedex
nejlevnější generické kamagra online