قدرة البرنامج على الحفاظ على نتيجة معينة حتى بعد الإجراءات المتكررة.على سبيل المثال، لنفترض أن لديك زرًا لا يفتح الباب إلا عند الضغط عليه. ليس لهذا الزر القدرة على غلق الباب، لذا فهو يظل مفتوحًا حتى عند الضغط عليه بشكل متكرر. إنه ببساطة يبقى في الحالة التي تم تغييرها إليها بواسطة الضغطة الأولى.ينطبق هذا المنطق نفسه على أساليب HTTP غير الفعالة. لن يكون للتشغيل على أساليب HTTP غير الفعالة بشكل متكرر أي تأثير إضافي يتجاوز التنفيذ الأولي.

يعد فهم القصور الذاتي أمرًا مهمًا للحفاظ على اتساق أساليب HTTP وتصميم واجهة برمجة التطبيقات (API). Idempotence له تأثير كبير على تصميم واجهة برمجة التطبيقات، لأنه يؤثر على كيفية تصرف نقاط نهاية واجهة برمجة التطبيقات عند معالجة الطلبات المقدمة من العملاء.

في هذا البرنامج التعليمي، سأشرح مفهوم الضعف والدور الذي يلعبه في بناء واجهات برمجة التطبيقات القوية والوظيفية. ستتعرف أيضًا على الأساليب الآمنة، وكيفية ارتباطها بالعجز الجنسي، وكيفية تنفيذ العجز الجنسي بطرق غير عاجزة.

المتطلبات الأساسية

قبل فهم وتنفيذ التكامل في تصميم واجهة برمجة التطبيقات (API)، من الضروري أن يكون لديك أساس متين في المجالات التالية:

  • مبادئ الراحة
  • أساسيات أساليب HTTP
  • تطوير واجهة برمجة التطبيقات
  • رموز حالة HTTP
  • أساسيات تطوير الويب.

مثال العجز

لنبدأ بمثال على العجز الجنسي في العمل. سنقوم بإنشاء دالة تستخدم طريقة DELETE لحذف البيانات من صفحة الويب:


from flask import Flask, jsonify, abort

app = Flask(__name__)

web_page_data = [
   {"id": 1, "content": "Row 1 data"},
   {"id": 2, "content": "Row 2 data"},
   # Add more rows as needed
]

@app.route('/delete_row/<int:row_id>', methods=['DELETE'])
def delete_row(row_id):
   # Find the row to delete
   row_to_delete = next((row for row in web_page_data if row["id"] == row_id), None)
   
   if row_to_delete:
       # Simulate deletion
       web_page_data.remove(row_to_delete)
       return jsonify({"message": f"Row {row_id} deleted successfully."}), 200
   else:
       abort(404, description=f"Row {row_id} not found.")

if __name__ == '__main__':
   app.run(debug=True)

من المتوقع أن تقوم هذه الوظيفة بحذف الصفوف التي اختارها المستخدم. الآن، نظرًا للطبيعة غير الفعالة لأسلوب الحذف، سيتم حذف البيانات مرة واحدة، حتى عند استدعائها بشكل متكرر. لكن المكالمات اللاحقة ستعرض خطأ 404 نظرًا لأن البيانات قد تم حذفها بالفعل بواسطة المكالمة الأولى.

دعونا نلقي نظرة على مثال آخر باستخدام طريقة GET. يتم استخدام أسلوب GET لاسترداد البيانات من أحد الموارد. لنقم بإنشاء دالة تستخدم طريقة GET لاسترداد اسم المستخدم:

import requests

def get_username():
    url = 'https://api.example.com/get_username'
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()['username']
        else:
            return None
    except requests.RequestException as e:
        print(f"Error occurred: {e}")
        return None

# Usage
username = get_username()
if username:
    print(f"The username is: {username}")
else:
    print("Failed to retrieve the username.")

في هذا المثال، نحدد get_username()الوظيفة التي ترسل طلب GET إلى نقطة نهاية API لاسترداد اسم المستخدم. إذا نجح الطلب، فإننا نستخرج اسم المستخدم من استجابة JSON ونعيده. ولكن في حالة حدوث أي خطأ أثناء الطلب، فإننا نتعامل معه ونعيده None.

الآن، تضمن الطبيعة غير الفعالة لطريقة GET أنه حتى إذا اتصلت get_username()عدة مرات، فسيتم جلب نفس اسم المستخدم من واجهة برمجة التطبيقات في كل مرة. ستكون النتيجة دائمًا هي نفسها وهي جلب اسم المستخدم من المورد.

طرق HTTP Idempotent مقابل طرق HTTP غير Idempotent:

تلعب أساليب HTTP أدوارًا حاسمة في تحديد كيفية جلب البيانات أو تعديلها أو إنشائها عند التفاعل مع واجهات برمجة التطبيقات. وتعد Idempotency أحد المفاهيم المهمة التي تؤثر على اتساق البيانات وموثوقيتها في الأساليب المستخدمة.

فيما يلي تفصيل للطرق المختلفة بناءً على عجزها.

الأساليب العاجزة:

  • يحصل
  • رأس
  • يضع
  • يمسح
  • خيارات
  • يتعقب

الأساليب غير العاجزة:

  • بريد
  • رقعة
  • يتصل

طرق آمنة

في مثالنا السابق، استخدمنا طريقة GET لاسترداد اسم المستخدم ولم يكن لذلك أي أثر جانبي على الخادم. وذلك لأنها طريقة آمنة.

الطريقة الآمنة هي نوع من الطريقة التي لا تقوم بتعديل حالة الخادم أو المورد الذي يتم الوصول إليه. بمعنى آخر، يقومون بعمليات القراءة فقط المستخدمة لاسترداد البيانات أو لتمثيل الموارد.

عند تقديم طلب باستخدام طريقة آمنة، لا يقوم الخادم بتنفيذ أي عمليات تعمل على تعديل حالة المورد. كما في مثالنا السابق، قمنا باسترجاع اسم المستخدم من صفحة الويب التي تعتبر المورد دون تغيير أي شيء في الخادم.

جميع الطرق الآمنة تكون غير فعالة تلقائيًا، ولكن ليست كل الطرق غير فعالة آمنة. وذلك لأنه على الرغم من أن الأساليب غير الفعالة تنتج نتائج متسقة عند استدعائها بشكل متكرر، إلا أن بعضها قد يستمر في تعديل حالة الخادم أو المورد الذي يتم الوصول إليه.

كما هو الحال في المثال الأول، تعتبر طريقة DELETE غير فعالة، لأن حذف المورد عدة مرات سيكون له نفس التأثير. ولكنها ليست آمنة، لأنها تغير حالة الخادم عن طريق إزالة المورد.

فيما يلي تصنيف لطرق HTTP بناءً على حالتها الآمنة:

الطرق الآمنة:

  • يحصل
  • خيارات
  • رأس

طرق غير آمنة:

  • يمسح
  • بريد
  • يضع
  • رقعة

لماذا POST ليس عاجزا؟

POST هي طريقة HTTP ترسل المعلومات إلى الخادم. عند تقديم طلب POST، فإنك تقوم عادةً بإرسال البيانات لإنشاء مورد جديد أو تشغيل إجراء من جانب الخادم. ولذلك، فإن تقديم نفس الطلب عدة مرات يمكن أن يؤدي إلى نتائج وآثار جانبية مختلفة على الخادم. يمكن أن يؤدي ذلك إلى بيانات مكررة، وبدء تشغيل موارد الخادم، وتقليل الأداء بسبب الإجراء المتكرر.

على عكس الأساليب غير الفعالة مثل GET وPUT وDELETE، التي لها نتائج متسقة بغض النظر عن التكرار، يمكن لطلبات POST أن تسبب تغييرات في حالة الخادم مع كل استدعاء.

غالبًا ما تنشئ طلبات POST موارد جديدة على الخادم. سيؤدي تكرار نفس طلب POST إلى إنشاء عدة موارد متطابقة، مما قد يؤدي إلى التكرار.

هذا مشابه لـ DELETE وهي طريقة غير فعالة ولكنها ليست طريقة آمنة. يعتبر حذف الإدخال الأخير في المجموعة باستخدام طلب DELETE واحد أمرًا غير فعال. ولكن إذا قام المطور بإنشاء وظيفة تحذف الإدخال الأخير، فسيؤدي ذلك إلى تشغيل عملية الحذف عدة مرات. سيكون لاستدعاءات الحذف اللاحقة تأثيرات مختلفة، حيث تقوم كل منها بإزالة إدخال فريد. هذا من شأنه أن يعتبر غير عاجز.

كيفية تحقيق بأساليب غيره

العجز ليس مجرد خاصية متأصلة في أساليب معينة – بل يمكن أيضًا تنفيذه كميزة لطريقة غير عاجزة.

وفيما يلي بعض التقنيات لتحقيق العجز الجنسي حتى مع الأساليب غير العاجزة.

المعرفات الفريدة

تعد إضافة معرفات فريدة لكل طلب أحد الأساليب الأكثر شيوعًا المستخدمة لتنفيذ الاختلال. إنه يعمل عن طريق تتبع ما إذا كانت العملية قد تم تنفيذها بالفعل أم لا. إذا كان مكررًا (طلب متكرر)، يعلم الخادم أنه قد تعامل بالفعل مع هذا الطلب ويتجاهله ببساطة، مما يضمن عدم حدوث أي آثار جانبية.

وفيما يلي مثال على كيفية عمله:

from uuid import uuid4
 
def process_order(unique_id, order_data):
    if Order.objects.filter(unique_id=unique_id).exists():
        return HttpResponse(status=409)  # Conflict
    order = Order.objects.create(unique_id=unique_id, **order_data)
    return HttpResponse(status=201, content_type="application/json")

# Example usage
post_data = {"products": [...]}
headers = {"X-Unique-ID": str(uuid4())}
requests.post("https://api.example.com/orders", data=post_data, headers=headers)

في مقتطف الكود هذا، نحدد وظيفة تسمى process_orderالتي تنشئ أوامر في واجهة برمجة التطبيقات (API)، باستخدام معرفات فريدة لتنفيذ الاختلال.

فيما يلي تفاصيل الكود:

استيراد منشئ المعرف الفريد:

from uuid import uuid4: يبدأ مقتطف الشفرة باستيراد الوظيفة uuid4من uuidالوحدة النمطية. تنشئ هذه الوظيفة معرفات فريدة تُستخدم لتحقيق التكافؤ في هذا الرمز.

تحديد process_orderالوظيفة:

def process_order(unique_id, order_data): يعرّف هذا السطر دالة تسمى Process_order والتي تأخذ وسيطتين:

  • unique_id: هذه سلسلة تمثل معرفًا فريدًا للطلب. وهذا يضمن عدم إنشاء أوامر مكررة بنفس المعرف.
  • order_data: هذا قاموس يحتوي على بيانات الطلب الفعلية، مثل معلومات المنتج وتفاصيل العميل.

التحقق من الطلبات الموجودة:

if Order.objects.filter(unique_id=unique_id).exists(): يتحقق هذا السطر من وجود طلب يحمل نفس المعرف الفريد بالفعل في قاعدة البيانات.

Order.objects.filter(unique_id=unique_id).exists()يستعلم عن نموذج الطلب للطلبات ذات المعرف الفريد المطابق ويتحقق من العثور على أي طلبات في نتيجة الاستعلام. إذا تم العثور على طلب، فهذا يعني أن نفس الطلب قد تمت معالجته بالفعل.

التعامل مع الطلبات الموجودة:

return HttpResponse(status=409): إذا كان الطلب بنفس المعرف الفريد موجود بالفعل، فسترجع الدالة على الفور استجابة HTTP مع رمز الحالة 409 الذي يشير إلى وجود تعارض. وهذا يمنع إنشاء أوامر مكررة.

إنشاء طلب جديد (إذا كان فريدًا):

order = Order.objects.create(unique_id=unique_id, **order_data ): يعمل هذا السطر فقط في حالة عدم العثور على أمر موجود.

Order.objects.create:يقوم بإنشاء كائن جديد في نموذج الطلب.

unique_id=unique_idيضبط سمة Unique_id للطلب الجديد على Unique_id المقدم.

order_data: ينشر بيانات order_data في القاموس كوسيطات للكلمات الرئيسية إلى مُنشئ نموذج الطلب، مع تعيين سمات أخرى ذات صلة مثل المنتجات ومعلومات العملاء.

إرسال استجابة النجاح:

return HttpResponse(status=201, content_type="application/json"): إذا كان إنشاء الطلب ناجحًا، ستعيد الوظيفة استجابة HTTP برمز الحالة 201 الذي يوضح الإنشاء الناجح. كما تحدد أيضًا نوع محتوى الاستجابة كـ JSON، على افتراض أنه قد يتم إرجاع بيانات الطلب بتنسيق JSON.

post_data = {"products": [...]}: طلب مثال، يحدد قاموسًا يحتوي على بيانات الطلب الفعلية، مثل قائمة المنتجات.

headers = {"X-Unique-ID": str(uuid4())}: يقوم هذا السطر بإنشاء قاموس يحتوي على رأس مخصص يسمى X-Unique-ID. يقوم بإنشاء سلسلة معرفات فريدة باستخدام uuid4() وإضافتها إلى الرأس.

requests.post("https://api.example.com/orders", data=post_data, headers=headers): يرسل هذا السطر طلب POST إلى نقطة نهاية واجهة برمجة التطبيقات (API) مع الرؤوس https://api.example.com/orders  المتوفرة .post_data

كيف يتم تنفيذه؟

ويتم ذلك عن طريق استخدام معرف فريد (unique_id)لكل طلب.

يتحقق مما إذا كان الطلب بنفس المعرف موجود بالفعل في قاعدة البيانات. إذا عادت صحيحة، فإنها تُرجع حالة تعارض 409. وبخلاف ذلك، فإنه ينشئ طلبًا جديدًا ويستجيب بالحالة 201 تم الإنشاء. المعرف الفريد يمنع الأوامر المكررة، مما يجعل النظام عاجزا.

التفويض القائم على الرمز المميز

التفويض المستند إلى الرمز المميز هو شكل من أشكال التفويض الذي يعين رموزًا مؤقتة لكل إجراء غير فعال. بمجرد اكتمال الإجراء، يتم إبطال الرمز المميز. إذا جاء نفس الطلب مرة أخرى بنفس الرمز المميز، فإن الخادم يتعرف عليه على أنه غير صالح ويرفض الطلب، وبالتالي يمنع الإجراءات المكررة.

// Generate a unique token for this action
const token = generateToken();

fetch("https://api.example.com/create-user", {
    method: "POST",
    body: JSON.stringify({ username, password }),
    headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
    },
})
    .then(response => {
        // Handle successful response
        if (response.ok) {
            // Do something with the successful response
        } else {
            // Handle non-successful response
        }
    })
    .catch(error => {
        // Handle error
        console.error("Error occurred:", error);
    })
    .finally(() => {
        // Invalidate token after successful action or in case of an error
        invalidateToken(token);
    });

// Simple implementation for generating a token
function generateToken() {
    return Math.random().toString(36).substr(2);
}

// Simple implementation for invalidating a token
function invalidateToken(token) {
    // Add your logic to invalidate the token, e.g., remove it from storage
}

فيما يلي تفاصيل الكود:

إنشاء رمز مميز:

const token = generateToken(): يستدعي هذا السطر وظيفة مسماة generateToken()(والتي من المفترض أن يتم تعريفها في مكان آخر) والتي تنشئ سلسلة رمزية فريدة. سيتم استخدام هذا الرمز المميز للترخيص والعجز.

إرسال POSTالطلب:

fetch("https://api.example.com/create-user", { ... }): يستخدم هذا الخط واجهة برمجة تطبيقات الجلب لإرسال طلب POST إلى نقطة نهاية واجهة برمجة التطبيقات https://api.example.com/create-user.

method: "POST": يحدد هذا طريقة HTTP كـ POST، مما يشير إلى نية إنشاء مستخدم جديد.

body: JSON.stringify({ username, password }): يحدد هذا نص الطلب بتفاصيل المستخدم مثل اسم المستخدم وكلمة المرور. يتم تحويل البيانات إلى تنسيق JSON قبل إرسالها.

headers: { Authorization:Bearer ${token}}: يؤدي هذا إلى تعيين رأس التفويض في الطلب. تتضمن قيمة الرأس الرمز المميز الذي تم إنشاؤه مسبوقًا بـ “Bearer”.

التعامل مع الاستجابة:

.then(response => { ... }): تحدد هذه الكتلة الكود الذي سيتم تنفيذه في حالة نجاح الطلب. يمكنك التعامل مع أشياء مثل تخزين معلومات المستخدم أو إعادة توجيه المستخدم عند إنشاء المستخدم بنجاح.

.catch(error => { ... }):تحدد هذه الكتلة الكود الذي سيتم تنفيذه إذا واجه الطلب خطأ. يمكنك التعامل مع أي رسائل خطأ أو التعامل مع سيناريوهات خطأ محددة هنا.

إبطال الرمز المميز:

invalidateToken(token): يستدعي هذا السطر وظيفة مسماة invalidateToken(token)(والتي من المفترض أن يتم تعريفها في مكان آخر) والتي من المحتمل أن تضع علامة على الرمز المميز المستخدم على أنه غير صالح. وهذا يضمن عدم إمكانية استخدام نفس الرمز المميز للطلبات اللاحقة، مما يزيد من ضمان العجز.

كيف يتم تنفيذ هذا العجز؟

يستخدم مقتطف الكود هذا تفويضًا يستند إلى الرمز المميز لتنفيذ الصلاحيات في طلب POST لإنشاء مستخدم على واجهة برمجة التطبيقات. إذا تم إرسال طلب إنشاء مستخدم عن طريق الخطأ عدة مرات، فسيتم إنشاء رمز مميز فريد جديد في كل مرة واستخدامه في رأس التفويض.

يمكن لخادم واجهة برمجة التطبيقات (API) التعرف على الرمز المميز الفريد والتحقق منه، وبما أن إجراء إنشاء المستخدم قد تم تنفيذه بالفعل (بافتراض نجاحه في المرة الأولى)، فلن يقوم بإنشاء مستخدمين مكررين بسبب الطلبات المتطابقة اللاحقة.

رأس علامة ETag:

رأس ETag (علامة الكيان) هو رأس HTTP يُستخدم للتحقق من صحة ذاكرة التخزين المؤقت على الويب والطلبات المشروطة. يتم استخدامه بشكل أساسي لطلبات PUT، التي تقوم فقط بتحديث الموارد إذا لم تتغير منذ آخر فحص.

عندما تريد تحديث أحد الموارد، يرسل لك الخادم ETag الخاص به والذي يتم بعد ذلك تضمينه في طلب PUT الخاص بك مع البيانات المحدثة. إذا لم تتغير علامة ETag (بمعنى أن المورد يظل كما هو)، فسيقبل الخادم التحديث. ولكن إذا تم تغيير ETag، فإن الخادم يرفض التحديث، مما يمنعه من الكتابة فوق تغييرات شخص آخر.

def update_article(article_id, content):
    # Get existing article and its ETag
    article = Article.objects.get(pk=article_id)
    etag = article.etag
    
    # Check if ETag matches with request header
    if request.headers.get("If-Match") != etag:
        return HttpResponse(status=409)  # Conflict
    
    # Update article content and generate new ETag
    article.content = content
    article.save()
    new_etag = article.etag
    
    # Return success response with updated ETag
    return HttpResponse(status=200, content_type="text/plain", content=new_etag)

في مقتطف الكود هذا، نحدد وظيفة تسمى update_articleتسمح لك بتحديث محتوى مقالة موجودة بناءً على معرفها والمحتوى الجديد. إنه يطبق العجز باستخدام تقنية رأس ETag.

وفيما يلي شرح خطوة بخطوة لكيفية عمله؛

الحصول على المقالة الموجودة وعلامة ETag الخاصة بها:

article = Article.objects.get(pk=article_id):يقوم هذا السطر بجلب المقالة باستخدام معرف المادة المقدم من قاعدة البيانات باستخدام نموذج المقالة.

etag = article.etag:يستخرج هذا السطر قيمة ETag من كائن المقالة المسترد. تعمل ETag كمعرف فريد للحالة الحالية للمقال.

التحقق من التطابق:

if request.headers.get("If-Match") != etag:يتحقق هذا السطر مما إذا كان رأس ETag المقدم في الطلب يتطابق مع ETag الخاص بالمقالة المستردة.

return HttpResponse(status=409): إذا لم تتطابق علامة ETag، فهذا يشير إلى أنه ربما تم تحديث المقالة بواسطة طلب آخر منذ أن قام العميل باسترجاع معلوماته. تقوم الدالة بإرجاع استجابة تعارض 409، مما يمنع تلف البيانات غير المقصود.

تحديث محتوى المقالة وإنشاء علامة ETag جديدة:

article.content = content:يقوم هذا السطر بتحديث محتوى المقالة بالمحتوى الجديد المستلم في الطلب.

article.save():يقوم هذا السطر بحفظ المقالة المحدثة مرة أخرى في قاعدة البيانات.

new_etag = article.etag:يقوم هذا السطر باسترداد ETag الجديد الذي تم إنشاؤه للمقال المحدث بعد حفظه.

إرجاع استجابة النجاح باستخدام ETag الجديد:

return HttpResponse(status=200, content_type="text/plain", content=new_etag): تقوم بإرجاع استجابة 200 OK ناجحة، بما في ذلك نوع المحتوى (“نص/عادي”) وعلامة ETag المحدثة للمقالة في نص الاستجابة.

كيف يتم تنفيذ العجز الجنسي؟

يضمن هذا الرمز أنه إذا تم إرسال نفس طلب التحديث عدة مرات بنفس ETag، فسيتم إجراء التحديث مرة واحدة فقط، مما يمنع التحديثات المكررة ويحافظ على اتساق البيانات. يتم بعد ذلك توفير علامة ETag الجديدة في الرد لمساعدة العميل على تتبع حالة المقالة للتفاعلات المستقبلية.

خاتمة

في هذا البرنامج التعليمي، سلطنا الضوء على الفرق بين الطرق الآمنة مثل GET، التي تسترد البيانات دون آثار جانبية، والطرق غير الفعالة مثل POST، والتي يمكن أن يكون لها نتائج مختلفة مع كل تكرار.

لقد استكشفنا أيضًا التقنيات التي يمكنك تطبيقها لتحقيق الضعف في الأساليب غير الفعالة، مع التركيز على أهمية تصميم واجهات برمجة التطبيقات التي تعطي الأولوية للاتساق والموثوقية.

نسعى دائما لتقديم كل ماهو ممتع لكم فاتمنى ان نكون عند حسن ظنكم جميعا.

فارجو دعمنا وتشجيعنا على تقديم الافضل بمشاركة الموضوع مع اصدقائكم على مواقع التواصل الاجتماعى فيس بوك تويتر عن طريق ازرار المشاركة اسفل التدوينة.

وترك تعليق داخل صندوق التعليقات تشجيعاُ لنا كل الود والاحترام لكم والى اللقاء فى تدوينة اخرى من تدوينات مدونة الشهادة .