اذهب إلى المحتوى

سامح أشرف

الأعضاء
  • المساهمات

    2934
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    56

إجابات الأسئلة

  1. إجابة سامح أشرف سؤال في كيفية إضافة ملفات robots.txt و sitemap.xml في فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكن القيام بهذا الأمر من خلال تغير مسار المجلد static الإفتراضي حتي يمكنك الوصول إلى محتويات المجلد المباشرة من خلال كتابة اسم الملف بعد النطاق domain مباشرة، ويتم هذا الأمر من خلال الخاصية static_url_path، حيث يتم إستخدام نص فارغ للتعبير عن المسار الرئيسي للمجلد:
    from flask import Flask app = Flask(__name__, static_folder='static', static_url_path='') يمكنك أيضًا أن تقوةم بعمل مسارات مخصصة لهذه الملفات، ثم إرسال محتوى هذه الملفات من خلال الدالة send_from_directory :
    from flask import Flask, request, send_from_directory app = Flask(__name__, static_folder='static') @app.route('/robots.txt') @app.route('/sitemap.xml') def static_from_root(): return send_from_directory(app.static_folder, request.path[1:]) بهذا الشكل يمكنك أن تصل إلى الملفات من خلال العناوين:
    https://www.example.com/robots.txt https://www.example.com/sitemap.xml  
  2. إجابة سامح أشرف سؤال في كيفية تخطي وسوم jinja في قوالب فلاسك Flask؟ كانت الإجابة المقبولة   
    توفر قوالب Jinja إمكانية عرض أي نصوص من خلال إستخدام من خلال ما يسمى بـ Escaping وذلك عبر إستخدام العنصر {% raw %} و {% endraw %} على النحو التالي:
    {% raw %} <ul> {% for item in seq %} <li>{{ item }}</li> {% endfor %} </ul> {% endraw %} بهذا الشكل سوف يتم عرض النص بنفس طريقة كتابته، ولن يتم ترجمة الكود من قِبل Jinja
    ملاحظة: لا يمكن أن يحتوي النص نفسه على الوسم {% endraw %} وذلك لأنه سيتم إعتباره نهاية الوسم {% raw %} بالتأكيد، لذلك يمكنك أن تقوم بإستخدام HTML Entities:
    &lbrace; { &percnt; % &rbrace; }  
  3. إجابة سامح أشرف سؤال في كيفية إستعمال التعابير النمطية  regular expressions في مسارات فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكن القيام بهذا الأمر من خلال إستخدام الصنف BaseConverter (المتوفر في werkzeug.routing) للقيام بإنشاء محول converter باسم regex على سبيل المثال، على النحو التالي:
    from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): def __init__(self, url_map, *items): super(RegexConverter, self).__init__(url_map) self.regex = items[0] app.url_map.converters['regex'] = RegexConverter ويمكنك أن تستخدمه بالشكل التالي:
    from flask import Flask, render_template from werkzeug.routing import BaseConverter app = Flask(__name__) class RegexConverter(BaseConverter): def __init__(self, url_map, *items): super(RegexConverter, self).__init__(url_map) self.regex = items[0] app.url_map.converters['regex'] = RegexConverter @app.route('/<regex("[a-zA-Z0-9]{2,5}"):uid>/', methods=['POST', 'GET']) def index(uid): return uid if __name__ == '__main__': app.run(debug=True) ستلاحظ أن كل المسارات التالية تعمل لأنها تحقق الشروط:
    http://127.0.0.1:5000/hi/ http://127.0.0.1:5000/12/ http://127.0.0.1:5000/123/ http://127.0.0.1:5000/hi123/ ... بينما المسارات التالية لن تعمل على الإطلاق:
    http://127.0.0.1:5000/ http://127.0.0.1:5000/a/ http://127.0.0.1:5000/%D9/ http://127.0.0.1:5000/hello-world/ http://127.0.0.1:5000/helloworld/ ... يمكنك أن تقرأ أكثر حول التعابير النمطية في هذه المقالة:
     
  4. إجابة سامح أشرف سؤال في كيفية إرسال بيانات من عنصر input إلى فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكنك القيام بهذا الأمر بطريقتين، الأولى عبر إستعمال JavaScript للحصول على البيانات من عنصر input وإرسالها في طلب إلى فلاسك Flask ليتم إستخدام هذه البيانات في عمل أي مهمة ثم إرجاع نتيحة معينة إلى صفحة الويب، أما الطريقة الثانية وهي الأسهل، حيث يتم إستخدام نموذج form لإرسال البيانات بطريقة POST إلى فلاسك Flask ثم يقوم بتنفيذ بعض المهام بإستخدام هذا النص ويرجع البيانات، كالتالي:
    في البداية تحتاج إلى تجهيز مسار route في فلاسك يستقبل هذه البيانات ويرجع قيمة معينة:
    @app.route('/process', methods=['POST']) def upper_text(): text = request.form['text'] processed_text = text.upper() return processed_text ومسار route آخر لعرض النموذج form:
    @app.route('/') def form(): return render_template('form.html') وسيحتوي الملف templates/form.html على نموذج واحد كالتالي:
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Upper Text</title> </head> <body> <form method="POST" action="/process"> <input name="text"> <input type="submit"> </form> </body> </html> بهذا الشكل سوف يتم الحصول على البيانات من النموذج form ثم سيتم تحويلها إلى أحرف كبيرة (يمكنك القيام بأي شيء في الدالة upper_text) ثم يتم إرجاع البيانات إلى المتصفح مرة أخرى.
    يمكنك أيضًا أن تستعمل قالب ليعرض البيانات المرجعة بدلًا من عرضها في شكل نصي فقط.
  5. إجابة سامح أشرف سؤال في كيف يمكنني إيقاف رسائل الـ Console في فلاسك Flask؟ كانت الإجابة المقبولة   
    يستخدم فلاسك Flask مكتبة logging لعرض الرسائل في الـ console ويمكنك أن تعدل على هذا الأمر وتجعله يعرض رسائل الخطأ فقط من خلال إضافة الكود التالي:
    import logging log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) بهذا الشكل لن يتم عرض أي رسائل إلا رسائل الخطأ فقط.
    هنا مثال لكيفية إستخدام هذا الكود في تطبيق فلاسك:
    from flask import Flask, request, render_template import logging app = Flask(__name__) # Stop logger log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) @app.route('/', methods=['POST', 'GET']) def index(): return render_template('home.html') if __name__ == '__main__': app.run(debug=True)  
  6. إجابة سامح أشرف سؤال في كيفية إرجاع رد فارغ Empty Response في فلاسك Flask؟ كانت الإجابة المقبولة   
    ما تحاول القيام به هو إرجاع رد برقم 204 ، حيث يعبر رقم الحالة 204 عن عدم وجود محتوى No Content، وللقيام بإرجاع رد برقم 204 يمكنك إستخدام الكود التالي:
    @app.route('/', methods=['POST', 'GET']) def index(): return ('', 204) أيضًا يمكنك أن تستعمل قيمة الخاصية NO_CONTENT كالتالي:
    import http @app.route('/', methods=['POST', 'GET']) def index(): return ('', http.HTTPStatus.NO_CONTENT) إن كنت تستعمل Python 2 فستحتاج إلى إستعمال الخاصية NO_CONTENT من المكتبة httplib:
    return ('', httplib.NO_CONTENT) لمزيد من المعلومات عن رموز الإجابة في HTTP:
    ويمكنك الإطلاع أيضًا على هذه الإجابة لمزيد من رموز الإجابة مع شرحها:
     
  7. إجابة سامح أشرف سؤال في كيفية تغير اسم المجلد static في فلاسك Flask؟ كانت الإجابة المقبولة   
    فلاسك Flask يقوم بتخزين مسار المجلد static في الخاصية static_url_path الموجودة في الكائن app.config ويمكن تغيرها إلى أي قيمة أخرى:
    from flask import Flask, render_template app = Flask(__name__) app.config.static_url_path = "/assets" @app.route('/', methods=['POST', 'GET']) def index(): return render_template("home.html") if __name__ == '__main__': app.run(debug=True) لكن لاحظ أنك إن قمت بتغير المسار إلى مسار آخر يستخدمه فلاسك بالفعل فلن يعمل، فعلى سبيل المثال لا يمكنك أن تقوم بإستخدام المجلد templates بدلًا من statics لأن فلاسك Flask سوف يقوم بإستخدام المجلد للقوالب وليس للملفات الثابتة static.
    ملاحظة: يمكنك إستخدام الخاصية static_url_path من داخل app مباشرة بدون الوصول إلى config:
    app = Flask(__name__) app.static_url_path = "/assets"  
  8. إجابة سامح أشرف سؤال في كيفية الحصول على user agent في فلاسك Flask؟ كانت الإجابة المقبولة   
    الـ user agent عبارة عن ترويسة header يتم إرسالها مع كل طلب، لذلك يمكنك الحصول عليه من خلال القاموس headers المتوفر في الكائن request:
    from flask import request, render_template app = Flask(__name__) @app.route('/', methods=['POST', 'GET']) def index(): print(request.headers.get('User-Agent')) return render_template("home.html") if __name__ == '__main__': app.run(debug=True) وإن كنت تستعمل الإصدار 2.0 أو أقل من Werkzeug فيمكنك أن تستخدم user_agent الموجود في الكائن request والذي يوفر لك مجموعة من الخصائص مثل الحصول على نظام التشغيل أو نوع المتصفح .. إلخ:
    @app.route('/', methods=['POST', 'GET']) def index(): print(request.user_agent.string) # Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.57 print(request.user_agent.platform) # windows print(request.user_agent.browser) # chrome print(request.user_agent.version) # 96.0.4664.110 print(request.user_agent.language) # None return render_template("home.html")  
  9. إجابة سامح أشرف سؤال في كيفية إضافة نص قبل كل المسارات تلقائيًا في فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكنك أن تستعمل ميزة blueprint التي يوفرها فلاسك Flask بشكل إفتراضي، حيث تقوم بإضافة كل المسارات إلى blueprint وتضيق نص مسبق إلى هذا المخطط blueprint، على النحو التالي:
    bp = Blueprint('api', __name__, template_folder='templates') @bp.route("/") def index(): return "Home page" @bp.route("/about") def about(): return "abour us page" بعد ذلك عليك أن تضيف هذا المخطط إلى التطبيق من خلال الكود التالي:
    app = Flask(__name__) app.register_blueprint(bp, url_prefix='/api/v1')
  10. إجابة سامح أشرف سؤال في كيفية تشغيل دالة معينة كل عدد معين من الثواني في مشروع فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكنك القيام بهذا الأمر من خلال إستخدام التابع BackgroundScheduler الموجود في الحزمة  APScheduler والتي تسمح لك بتنفيذ دالة معينة كل فترة من الزمن مثل ما يقوم به corn بالضبط.
    أولًا عليك تثبيت الحزمة من خلال الأمر التالي:
    pip install APScheduler ثم يمكنك أن تقوم بإستخدامها على النحو التالي:
    import time import atexit from flask import Flask, request, render_template from apscheduler.schedulers.background import BackgroundScheduler app = Flask(__name__) scheduler = BackgroundScheduler() def date_time(): print("Working at: ", time.strftime("%A, %d. %B %Y %I:%M:%S %p")) @app.route('/', methods=['POST', 'GET']) def index(): scheduler.add_job(func=date_time, trigger="interval", seconds=3600) scheduler.start() return render_template("home.html") if __name__ == '__main__': app.run(debug=True) كما يمكنك أن تقوم بإيقاف كل العمليات التي تعمل في الخلفية من خلال إستخدام التابع register:
    atexit.register(lambda: scheduler.shutdown()) ويمكنك أن تضيف الكود السابق في مسار مختلف ليتم إيقاف العمليات عند زيارته:
    @app.route('/shutdown') def shutdown(): atexit.register(lambda: scheduler.shutdown()) return "closed"
  11. إجابة سامح أشرف سؤال في خطأ not JSON serializable عند تحويل نتيجة من SQLAlchemy  إلى JSON في فلاسك Flask؟ كانت الإجابة المقبولة   
    سبب هذا الخطأ هو أنك لم تقم بتنفيذ الإستعلام من الأساس لذلك يتم إرجاع كائن sqlalchemy.BaseQuery وليس قائمة بالمستخدمين، ولحل هذه المشكلة يمكنك أن تستعمل التابع all على النحو التالي:
    return jsonify(json_list = qryresult.all()) بهذا الشكل يمكنك أن تقوم بإرجاع قائمة المستخدمين.
    يمكنك أيضًا عمل تسلسل serialize كالتالي:
    return jsonify(json_list=[i.serialize for i in qryresult.all()])
  12. إجابة سامح أشرف سؤال في تطبيق فلاسك Flask يقوم بتشغيل نفسه مرتين؟ كانت الإجابة المقبولة   
    هذا الأمر يحدث بسبب المحمل Werkzeug والذي يقوم بإنشاء عملية process جديدة للمشروع، لكي يقوم بإغلاق هذه العملية وإعادة تشغيلها عندما يتم تغير أي أكواد خاصة بالمشروع، وبالتالي يتم إعادة تحميل الخادم بسرعة دون الحاجة إلى إعادةى تشغيله يدويًا في كل مرة، وبالتأكيد هذا الأمر يحدث فقط في بيئة التطوير Development Environment ولا يتم إستخدامه في بيئة الإنتاج Production Environment، ويمكنك التأكد من هذا الأمر من خلال قراءة الكود الخاص بالدالة restart_with_reloader في الملف werkzeug/_reloader.py حيث ستجد أنه يتم إستعمال التابع ()subprocess.call للقيام بإنشاء عملية جديدة والتحكم فيها في كل مرة يتم تعديل كود المشروع.
    تستطيع إيقاف هذا السلوك من خلال تمرير المعامل use_reloader بقيمة Flase إلى التابع app.run كالتالي:
    if __name__ == '__main__': print('# Starting #') app.run(debug=True, use_reloader=False) و من خلال الأمر flask على النحو التالي:
    FLASK_DEBUG=1 flask run --no-reload يمكنك أيضًا أن توقف هذا السلوك من خلال تشغيل التطبيق في وضع الإنتاج Production Mode عن طريق إزالة المعامل debug :
    if __name__ == '__main__': print('# Starting #') app.run() في كل الحالات السابقة لن يعمل التطبيق إلا مرة واحدة، ولكن ستخسر ميزة إعادة تشغيل الخادم بشكل تلقائي وعليك حينها أن تقوم بإعادة تشغيله بشكل يدوي.
  13. إجابة سامح أشرف سؤال في كيفية إرجاع صورة في الرد response من خلال فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكنك أن تقوم بهذا الأمر من خلال إستخدام الدالة send_file والتي تقوم بإستقبال مسار الصورة (أو أي ملف) كمعامل أول، ويمكنك تمرير نوع الملف (لكي يتعرف عليه المتصفح ويعرضه بالطريقة الصحيح) من خلال تمرير المعامل mimetype، على النحو التالي:
    @app.route('/user_image') def user_image(): # ... return send_file('./path/to/image.jpg', mimetype='image/jpeg') بهذا الشكل يمكنك أن تقوم بإعادة صورة (أو أي ملف آخر) بعد التأكد من بيانات المستخدم أو أي شيء آخر.
  14. إجابة سامح أشرف سؤال في خطأ cannot import name Flask في مشروع فلاسك Flask؟ كانت الإجابة المقبولة   
    سبب المشكلة الأولى أنك تقوم بإستخدام ملف باسم flask.py وعندما تستدعي الكائن Flask أو request من المكتبة flask فإن بايثون تحاول إستدعاء هذه الكائنات من هذا الملف flask.py وبالتالي يفشل الكود في العثور على هذه الكائنات، ولحل المشكلة يجب إعادة تسميه الملف لأي شيء آخر غير flask.py مثل app.py
    الأمر الآخر هو أنك يجب أن تقوم بتثيت flask في البيئة الإفتراضية وليس بشكل عام في نظام التشغيل، وذلك من خلال تفعيل البيئة الإفتراضية
    source bin/activate ثم تنفيذ الأمر التالي لتثبيت فلاسك Flask:
    pip install flask الآن يمكنك تشغيل المشروع بدون مشكلة:
    python app.py أو من خلال الأمر flask:
    flask run  
  15. إجابة سامح أشرف سؤال في لا يمكن الوصول إلى ملفات CSS أو JavaScript في مشروع فلاسك Flask؟ كانت الإجابة المقبولة   
    تطبيقات فلاسك Flask تحتاج لإضافة كل الملفات من نوع Static مثل ملفات CSS و ملفات JavaScript إلى مجلد باسم static والوصول إلى هذه الملفات من خلال الدالة url_for وتمرير القيمة static إليها كمعامل أول ومسار الملف المراد تحميله داخل المجلد static، وبالتالي ستكون بنية المشروع على النحو التالي:
    /app - app_runner.py /services - app.py /templates - home.html /static /styles - style.css  أما في الملف home.html فيجب أن تقوم بتعديل مسار الملف ليكون كالتالي:
    <link rel="stylesheet" href="{{ url_for('static', filename='styles/style.css') }}"> أو إذا كان ملف JavaScript:
    <script srd="{{ url_for('static', filename='js/script.js') }}"></script>
  16. إجابة سامح أشرف سؤال في خطأ ImportError: No module named MySQLdb في فلاسك Flask؟ كانت الإجابة المقبولة   
    تحدث هذه المشكلة لأنك لم تقم بتثبيت الحزمة python-mysql بشكل صحيح، يمكنك تثبيتها من خلال الأمر التالي:
    sudo apt-get install python-mysqldb أيضًا يمكنك أن تستعمل الحزمة pymysql والتي سوف تؤدي نفس الغرض وتستطيع تثبيتها من خلال الأمر التالي:
    pip install pymysql ثم يجب تعديل إعدادات المشروع من خلال تغير مسار قاعدة البيانات، على النحو التالي:
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://.....'  
  17. إجابة سامح أشرف سؤال في كيف يمكنني الحصول على جسم الطلب Request Body في فلاسك Flask؟ كانت الإجابة المقبولة   
    تستطيع إستخدام التابع ()request.get_data والذي يتم إستخدامه من قِبل كلٍ من request.data و request.form و request.json حيث يتم جلب البيانات وعمل parse لها حسب قيمة الترويسة content-type في رأس الطلب
    على سبيل المثال إن كانت قيمة الترويسة Content-Type تحمل أي قيمة من القيم التالية:
     
    multipart/form-data application/x-www-form-urlencoded application/x-url-encoded فسوف يتم جلب البيانات وتحليلها وتخزينها في request.form، وستكون قيمة كلٍ من reqeust.data و request.json فارغة حينها.
    يمكنك إستعمال التابع ()request.get_data على النحو التالي:
    @app.route('/', methods=['POST']) def index(): print(request.get_data()) # ... لاحظ أنك إن إستعملت أيًا من التوابع request.data و request.form و request.json فسوف يقوم التابع ()request.get_request بإرجاع قيمة فارغة، لذلك عليك أن تقوم بتخزين جسم الطلب في متغير في بداية الدالة.
  18. إجابة سامح أشرف سؤال في كيفية تفعيل CORS في تطبيق فلاسك Flask؟ كانت الإجابة المقبولة   
    يمكنك حل هذه المشكلة من خلال تثبيت الحزمة flask-cors والتي سوف تقوم بضبط إعدادات CORS في مشروعك، لتثبيت الحزمة أستخدم الأمر التالي:
    pip install -U flask-cors الآن يمكنك إستخدامها في الكود على النحو التالي:
    from flask import Flask from flask_cors import CORS, cross_origin app = Flask(__name__) # إعداد الحزمة cors = CORS(app) app.config['CORS_HEADERS'] = 'Content-Type' @app.route("/") @cross_origin() # أضف هذا السطر لكل المسارات التي تريد الوصول إليها عبر Ajax def index(): return "Home Page" يمكنك أيضًا أن تستخدم دالة لتقوم بتعديل قيمة الترويسة header التي تحمل الاسم Access-Control-Allow-Origin لتكون قيمتها * لكل المسارات من خلال الكود التالي:
    @app.after_request def after_request(response): header = response.headers header['Access-Control-Allow-Origin'] = '*' return response بهذا الشكل سوف يتم إيقاف CORS لكن لا ينصح بهذا الأمر لأن الموقع قد يتعرض لهجمات تزوير الطلبات من قِبل بعض المخترقين Hackers.
  19. إجابة سامح أشرف سؤال في كيفية الحصول على كل المسارات الموجودة في تطبيق مبني بإستخدام فلاسك Flask؟ كانت الإجابة المقبولة   
    فلاسك Flask يقوم بتخزين كل المسارات في  app.url_map والذي هو عبارة عن مثيل من الكائن werkzeug.routing.Map ، ويمكنك المرور على كل مسار من خلال إستخدام التابع iter_rules، على النحو التالي:
    from flask import Flask, request, url_for, Response app = Flask(__name__) @app.route('/users/<user_id>') def index(user_id): pass @app.route('/foo') def foo(): routes = [] for rule in app.url_map.iter_rules(): routes.append(rule.endpoint) print(routes) # ['foo', 'static', 'index'] return "printed routes", 200 app.debug = True app.run(debug=True) بهذا الشكل يمكنك الحصول على كل المسارات. كما يمكنك أن تقوم بالحصول على المسارات من خلال سطر الأوامر عبر إستدعاء ملف المشروع الرئيسي وطباعة قيمة app.url_map:
    python >>> from app import app >>> app.url_map ['foo', 'static', 'index'] أيضًا تستطيع تنفيذ الأمر flask routes للحصول على قائمة من المسارات وأنواعها:
    > flask routes Endpoint Methods Rule -------- ------- ----------------------- foo GET /foo index GET /users/<user_id> static GET /static/<path:filename>
  20. إجابة سامح أشرف سؤال في كيفية حذف سطر من جدول بإستخدام id عبر SQLAlchemy في فلاسك Flask؟ كانت الإجابة المقبولة   
    يوفر الكائن query التابع filter_by والتابع filer ، ويمكن إستخدام أي منهما للقيام بهذه المهمة، على النحو التالي:
    التابع filter:
    User.query.filter(User.id == 123).delete() التابع filter_by:
    User.query.filter_by(id=123).delete() لكن إن كان الجدول users مرتبط بجدول آخر ويتم إعداد إستخدام CASCADE في العلاقة، فعليك أن تستخدم الطريقة الموجودة في سؤال أو الطريقة التالية:
    user = db.session.query(User).filter(User.user_id==123).first() db.session.delete(user) كما يجب أن تقوم بعمل commit لهذه التغيرات من خلال السطر التالي:
    db.session.commit() لمزيد من المعلومات عن التابع filter يمكنك أن تلقي نظرة على هذه المقالات هنا:
     
  21. إجابة سامح أشرف سؤال في كيفية تغير قيمة الترويسة content-type في فلاسك Flask؟ كانت الإجابة المقبولة   
    بالتأكيد، يوفر فلاسك Flask طريقة لتعديل الـ headers قبل إرسال الرد response إلى العميل وذلك من خلال إستخدام Response.headers، كالتالي:
    from flask import Response @app.route('/home') def home(): xml = '<foo>content</foo>' r = Response(response=xml, status=200, mimetype="application/xml") r.headers["Content-Type"] = "text/xml; charset=utf-8" return r كما أن الكائن Response يقبل معامل باسم content_type لتغير نوع البيانات الموجودة في الرد response، ويمكنك إستخدامها كالتالي:
     
    r = Response(response=xml, content_type='text/xml; charset=utf-8') أيضًا فلاسك Flask يقبل إرجاع قائمة من الكائنات عند إستخدام return في نهاية المسار، حيث يكون العنصر الأول في هذه القائمة عبارة عن المحتوى المراد إرجاعه إلى العميل، والعنصر الثاني هو رقم حالة الطلب request status code، والعنصر الثالث هو ترويسات الرد response headers، على النحو التالي:
    from flask import Response @app.route('/home') def home(): xml = '<foo>content</foo>' return xml, 200, {'Content-Type': 'text/xml; charset=utf-8'}  
  22. إجابة سامح أشرف سؤال في كيف نستطيع توظيف رمز التوكين والاستفادة منه كانت الإجابة المقبولة   
    هناك فرق بين إستخدام session و JWT ولكل منها إستخداماته، في بعض الحالات يكون إستخدام الجلسات session مثل المواقع البسيطة التي تستخدم خادم واحد فقط ولا تحتاج إلى عمل API خاص يحتاج إلى عملية مصادقة. على الجانب الآخر فإن إستخدام JWT يكون مفيد عندما يكون للموقع أكثر من خادم (مثل كل المواقع الكبيرة التي تقوم بتخزين ملفاتها وبياناتها في أكثر من دولة)، وبالتالي لا يمكن التحقق من session id الخاص بالمستخدم لأنه يتم تخزينه على خادم واحد وسيكون من الصعب مشاركته إلى أكثر من خادم، وهنا يأتي دور JWT حيث يتم تشفير بعض البيانات بإستخدام Secret Key ويتم تخزين هذا الرمز token على جهاز العميل (في المتصفح في شكل Cookies أو Local Storage) وكلما قام المستخدم بطلب أحد الصفحات يتم التأكد من الرمز token من خلال فك تشفيره.
    هنا شرح لكيف تعمل الجلسات Session، وكذلك تقينة Json-Web-Token (JWT):
    الجلسات Sessions
    يمكنك أن تتخيل أن الجلسة session عبارة عن مصفوفة من البيانات كالتالي:
    $session = [ "session_id" => "123456", "user_id" => "q1w2e3", "user_name" => "Sameh" ]; من الكود السابق يتضح أن الجلسة عبارة عن مجموعة من البيانات (مثل اسم المستخدم والمعرف الخاص به id) ويتم توليد id لهذه الجلسة session id بشكل عشوائي، وعندما يقوم أحد المستخدمين بتسجيل الدخول إلى الموقع، يقوم الخادم بتوليد هذه المصفوفة من البيانات (الجلسة session) ويقوم بحفظها في أحد الأماكن (ملف أو قاعدة بيانات أو memcached / redis أو حتى في مصفوفة في الذاكرة)، ثم يتم إرسال معرف الجلسة session id إلى المستخدم ليتم تخزينة في المتصفح في أحد الـ Cookies (تتم هذه العملية تلقائيًا في أغلب الأحيان).
    الآن يوجد لدينا session في الذاكرة ويوجد لدى العميل session id يتم إرساله بشكل تلقائي مع كل طلب، ويقوم الخادم بالتأكد من وجود جلسة session بهذا الـ id وإذا وجدها يقوم بالسماح بإكمال الطلب وإذا لم يجد أي جلسة لها هذا المعرف id يتم تحويل المستخدم لصفحة تسجيل الدخول ليحصل على جلسة جديدة.
    هنا صورة قد توضح كيف تتم العملية بالكامل بشكل مبسط:

    الآن ماذا سوف يحدث إذا قام أحد المخترقين بسرقة معرف الجلسة من متصفح العميل وقام بزراعة هذه البيانات في المتصفح الخاص به (متصفح الهاكر) ثم قام بإرسال بعض الطلبات إلى الخادم على أساس أنه هو العميل الحقيقي؟ سوف تتم كل الطلبات بشكل سليم وسوف يستطيع المخترق بأن يقوم بكل العمليات بدون أي صعوبة، وهذه الطريقة تم إستخدامها على نطاق واسع خلال الأشهر الماضية لسرقة العديد من قنوات اليويتيوب الكبيرة، وذلك من خلال إختراق المتصفح الخاص به (عبر تثبيت أحد البرامج أو الإضافات) والحصول على الـ Cookies الخاصة ببعض المواقع، تسمى هذه الطريقة في الإختراق بـ session hijacking.
    ماذا سوف يحدث إذا كان لدى الموقع أكثير من خادم لحفظ البيانات مثل المواقع الكبيرة التي تحتاج إلى أكثر من خادم لحفظ البيانات؟ لن يمكننا إستخدام الجلسات بشكل مباشر لأن بيانات الجلسة الواحدة يتم تخزينها في ذاكرة أحد الخاودم (أو في قاعدة البيانات) ولا يمكن أن تتم مشاركتها إلى كل الخوادم، وبالتالي يجب أن يكون هناك خادم خاص بتخزين بيانات الجلسات ويقوم كل خادم بالإتصال به والتأكد من الجلسة في كل طلب مما سوف يؤثر على أداء التطبيق بشكل عام.
    يمكنك معرفة المزيد عن الجلسات Session من خلال موسوعة حسوب من هنا (الجلسات في Laravel) و من هنا (الدالة session_start في php)
    رمز JWT
    رمز JWT يعمل بطريقة مختلفة بعض الشيء، فعندما يقوم المستخدم بتسجيل الدخول، يقوم الخادم بتوليد رمز Token ويرسله إلى المستخدم وفي كل طلب يرسله العميل يجب أن يرسل معه هذا الرمز ليقوم الخادم بفك تشفير هذا الرمز والتحقق من المستخدم، وتتم هذه العملية بالكامل بدون تخزين أي رموز token في الذاكرة أو في قاعدة بيانات.
    هنا صورة توضح الطريقة التي تعمل بها رموز JWT:

    ما هي مكونات رمز JWT
    يتكون الرمز من ثلاثة أجزاء على النحو التالي:
    رأس Header: يحتوي على الخوارزمية المستعملة في التشفير ونوع الرمز: { "alg": "HS256", "typ": "JWT" }  
    حمولة payload: تحتوي على بيانات المستخدم (بدون كلمة المرور): { "user_id": "1234567890", "name": "John Doe", "iat": 1516239022 }  
    توقيع signature: يحتوي على ناتج تشفير كلًا من الرأس header والحمولة payload بإستخدام خوارزمية مثل HMAC-SHA256 بإستخدام Secret Key (يمكن إعتبارها كـكلمة سر خاصة بالخادم فقط، ولا يتم الكشف عنها بأي شكل من الأشكال) ويكون الناتج في النهاية بالشكل التالي:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c وإذا تم تغير أي جزء من البيانات سوف تحصل على رمز مختلف كليًا، وهذا الأمر يضمن سلامة البيانات وأنه لم يتم التلاعب بها قبل إرسالها. يمكنك إستخدام الموقع jwt.io لتجربة كيف يعمل JWT وتجربة تغير أي جزء من البيانات وملاحظة الرمز الذي يتم توليده.
    عندما يستقبل الخادم هذا الرمز يقوم بإستخدام نفس الخوارزمية ولكن لفك تشفير الرمز بإستخدام Secret Key، وإذا تمت عملية فك التشفير بنجاح نكون قد نجحنا في التحقق من المستخدم.
    الآن ماذا سوف يحدث إذا كان الموقع يستعمل أكثر من خادم لإدارة البيانات؟ سوف يعمل كل شيء على مايرام لأن الخادم يقوم بفك تشقير الرمز الذي يصل إليه من المستخدم ولا يحتاج إلى قاعدة بيانات مشتركة لتخزين بيانات الجلسة أو أي شيء من هذا القبيل.
    ماذا سوف يحدث إذا قام أحد المخترقين بالوصول إلى رمز JWT الموجود لدى أحد المستخدمين؟ سوف يكون بأمكان المخترق عمل كل الطلبات وكأنه هو المستخدم الحقيقي، ولحل هذه المشكلة يتم جعل الرمز صالح لفترة قصيرة للغاية (بضع دقائق فقط) ويتم إنشاء Refresh Token (رمز يستخدم للحصول على رمز Token جديد) وكلما إنتهت هذه الفترة يتم إستخدام الـ refresh token للحصول على رمز جديد (بعض المواقع تقوم بإرسال رمز refresh token جديد أيضًا في هذه العملية، ولا يكون الرمز refresh token صالح للإستخدام مرة أخرى)، ويكون للـ refresh token فترة صلاحية أيضًا ولكن أكبر من رمز token العادي (قد تكون عدة أيام أو حتى أسابيع)، بعد إنتهاء هذه الفترة يجب على المستخدم أن يقوم بتسجيل الدخول مرة أخرى.
    ماذا سوف يحدث إذا قام المخترق بالوصول إلى كلٍ من رمز Token وكذلك Refresh Token؟ سوف يكون بإمكانه توليد رموز Token جديدة ويقوم بإستعمالها وكأنه المستخدم الحقيقي إلى أن تنتهي صلاحة الـ refresh token ويحتاج إلى تسجيل الدخول مرة أخرى.
    ملاحظة: إذا إستطاع أحد المخترقين الوصول إلى بيانات تسجيل الدخول الخاصة بالمستخدم (البريد الإلكتروني وكلمة السر) فسيكون بإمكانه توليد رموز token و refresh token كذلك إلى أن يقوم المستخدم بتغير كلمة السر الخاصة به.
    ملاحظة: الـ refresh token قد يتم تخزينه على الخادم، وعندما يقوم أحد المستخدمين بطلب رمز token جديد عبر الـ refresh token الخاص به، يقوم الخادم بالتحقق من أن الـ refresh token l موجود بالفعل لديه، وبالتالي يمكن التحكم في الـ refresh token وحذفه في حالة تم إستخدامه مرة واحدة أو قام المستخدم بتغير بياناته، أو قام بطلب رمز token من عنوان ip مختلف  .. إلخ، مما يوفر أمان أكبر للـ refresh token من الإستخدام حتى بعد سرقته.
    يجب أن تعلم أيضًا أنه توجد دائمًا طريقة لإختراق أحد المستخدمين وإرسال بعض الطلبات على أساس أن المستخدم الحقيقي هو من طلبها، ويتوقف الأمر على العميل نفسه وفي عادات الحماية التي يتبعها مثل عدم تحميل ملفات من مواقع غير أمنة أو عدم إستخدام http لإرسال أي طلبات أو إستخدام كلمة سر مختلفة لكل موقع وتغيرها كل فترة من الزمن .. إلخ. ويمكن للموقع مساعدة المستخدمين في هذا الأمر من خلال إجبار المستخدم على إختيار كلمة سر قوية وإجباره على تغيرها كل فترة من الزمن وعدم إستقبال أي طلبات من نوع http تأتي إلى الخادم .. إلخ.
  23. إجابة سامح أشرف سؤال في كيفية تمرير أكواد HTML إلى قالب Jinja2 في فلاسك Flask؟ كانت الإجابة المقبولة   
    هذه الخاصية تسمى autoescaping  وهي موجودة بشكل إفتراضي لحماية الموقع من ثغرات مثل XSS ، ولكن تدعم قوالب jinja إيقاف هذه الخاصية في جزء معين من خلال إضافة المرشح safe| إلى اسم المعامل، على النحو التالي:
     
    {{ htmlcode|safe }} وإن كان لديك العديد من المتغيرات التي تريد عرضها فيمكنك أن تقوم بإيقاف الخاصية autoescaping في جزء من القالب على النحو التالي:
     
    {% autoescape false %} {{ something }} <p>{{ something_else }}</p> <b>{{ something_important }}</b> {% endautoescape %} الطريقة الأخرى هي إستخدام MarkupSafe، حيث توفر الدالة Markup التي تقوم بنفس مهمة المرشح safe| السابق، يمكنك تحميل المكتبة من خلال الأمر التالي:
    pip install -U MarkupSafe ثم يمكنك أن تستعملها على النحو التالي
    from markupsafe import Markup htmlcode = Markup('<p>HTML Content</p>') الآن يمكنك تمرير المتغير htmlcode إلى أي قالب وسوف يتم عرض محتواه بدون مشكلة وبدون إستعمال المرشح safe|
    ملاحظة: لا تقم بإستخدام هذه الطرق إلا إذا كنت متأكد من محتوى htmlcode السابق، وذلك لكي لا يتعرض الموقع لهجمات cross-site scripting (XSS)
  24. إجابة سامح أشرف سؤال في كيفية إستخدام الدالة url_for لعمل مسارات ديناميكية في فلاسك Flask؟ كانت الإجابة المقبولة   
    الدالة url_for تستقبل معاملات keywords حيث يمكنك أن تقوم بتمرير قيمة user_id مباشرة كمعامل للدالة url_for وسوف يتم توليد العنوان بالكامل:
    @app.route('/foo') def foo(): print(url_for('index', user_id = 123)) # /users/123 return "Foo Page" سوف يتم تعويض قيمة المتغير user_id في المسار بالقيمة 123، وإذا كان المتغير user_id إختياري فليس عليك أن تقوم بتمرير أي قيمة.
    يمكنك معرفة المزيد عن الدالة url_for من خلال هذه المقالة:
  25. إجابة سامح أشرف سؤال في كيفية الحصول على الـ headers بإستخدام فلاسك Flask؟ كانت الإجابة المقبولة   
    يوفر فلاسك Flask إمكانية الوصول إلى http headers من خلال الكائن request حيث يحتوي على قاموس بإسم headers ويمكنك أنت تستخدمه للوصول إلى الترويسات headers:
    from flask import request @app.route('/') def index(): print(request.headers['authorization']) return "hello, world" ملاحظة: إذا لم يكن الـ authorization header موجود ضمن الترويسات المرسلة فسوف تحصل على خطأ KeyError، ولكي تتخطى هذا الخطأ يمكنك أن تستعمل التابع get للحصول على الترويسة وإذا لم يكن موجود سوف تحصل على None:
    from flask import request @app.route('/') def index(): print(request.headers.get('authorization')) return "hello, world" أو يمكنك التأكد من خلال إستخدام المعامل in في جملة شرطية كالتالي:
    from flask import request @app.route('/') def index(): if 'authorization' in request.headers: print(request.headers['authorization']) return "hello, world"  
×
×
  • أضف...