پادیوم بلاگ
ساخت API با فلسک

آموزش ساخت API با Flask

رضا دهقان
تکنولوژی ، مقالات

فریم‌ورک فلسک (Flask) یکی از فریم‌ورک‌های محبوب برای ساخت اپلیکیشن‌های وب و APIها است. این فریم‌ورک‌ مجموعه‌ای از عملکردهای پایه‌ای را در اختیار توسعه‌دهنده‌ها قرار می‌دهد، اما طوری طراحی شده که می‌توان بسته به نیاز پروژه یا شخص آن را توسعه داد. در این پست با هم نحوه ساخت یک REST API با استفاده فلسک را به شما آموزش می‌دهیم. اما اگر دوست دارید نحوه ساخت API با استفاده از پایتون را یاد بگیرید توصیه می کنم پست قبلی را ببینید:

این API که امروز آن را می‌سازیم، قرار است اطلاعاتی در مورد زبان‌های مختلف برنامه‌نویسی را در اختیار کاربران قرار دهد. داده‌های این API از تحقیق هیلل وین (Hillel Wayne) با موضوع زبان‌های برنامه‌نویسی تاثیر گذار گرفته شده است. در پایان این آموزش API ساخته‌شده توسط شما به کاربران اجازه می‌دهد تا اعمال زیر را انجام دهند:

  • دریافت تمام زبان‌های برنامه‌نویسی ذخیره شده در API (دستور GET)
  • دریافت یک داده از یک زبان برنامه‌نویسی (دستور GET)
  • فیلتر کردن داده‌های زبان‌های برنامه‌نویسی بر اساس سال انتشار
  • استفاده از دستورات POST، PUT و DELETE برای ایجاد تغییر در داده‌های API

نکته: دستورات GET، POST، PUT و DELETE در واقع متدهای درخواست HTTP هستند که برای انجام یک عمل خاص بر روی داده استفاده می‌شوند.


چطور با استفاده از فلسک اندپوینت‌های REST API را بسازیم؟

پروتکل REST به کلاینت‌ها اجازه دسترسی به داده‌های ذخیره‌شده در پایگاه داده و انجام عمل بر روی این داده‌ها را می‌دهد. این دستورات را با نام CRUD (مخفف عبارت‌های Create، Read، Update، Delete) می‌شناسند. در ادامه نحوه ساخت دستورات CRUD برای API فلسک خودتان را یاد می‌گیرید.

نصب فلسک

نکته: در این قسمت از ابزار virtualenv برای ساخت یک محیط مجازی بر روی سیستم شما استفاده می‌شود.

یک دایرکتوری برای ذخیره کدها بسازید و وارد آن شوید:

mkdir example_app && cd example_app

داخل دایرکتوری example_app، یک فایل با نام prog_lang_app.py بسازید:

mkdir example_app && cd example_app
  touch prog_lang_app.py

با استفاده از دستور زیر یک محیط مجازی را ایجاد و فعال کنید:

python3 -m venv venv
  . venv/bin/activate

برای اجاری یک سرور فلسک، ابتدا باید با استفاده از دستور pip فلسک را نصب کنید:

pip install flask

ساخت اندپونیت لیست (List Endpoint) در فلسک

سرویس‌هیا RESTful به طور معمول دو اندپوینت برای دریافت داده‌ها (GET) دارند. یکی از اندپوینت‌ها همه داده‌ها را لیست کرده و یا بر اساس ویژگی‌های خاصی آن‌ها را فیلتر می‌کند. دومین اندپوینت جزییات یک داده خاص را دریافت می‌کند. در ادامه، شما دو اندپوینت برای دریافت داده‌ها در API خود می‌سازید. برای راحتی کار نام این اندپوینت‌ها را list و details می‌گذاریم. 

نکته: تمام دستورات زیر فایل prog_lang_app.py را ویرایش می کند.

در ادیتور کد خودتان فایل prog_lang_app.py را باز کرده و کدهای زیر را به آن اضافه کنید:

from flask import Flask
app = Flask(__name__)

این کدها فلسک را ایمپورت کرده و اپلیکیشن را نمونه‌سازی می کند. شما می توانید کلاس Flask را نمونه‌سازی کنید و آن را به یک متغیر تخصیص دهید.

نکته: هرچند REST APIها به طور معمول داده را از پایگاه داده دریافت می‌کنند، اما این آموزش جزییات یکپارچه‌سازی با پایگاه داده را پوشش نمی‌دهد.

یک منبع داده درون‌حافظه‌ای کوچک برای ذخیره داده‌های مربوط به زبان برنامه‌نویسی بسازید. کدهای زیر را زیر کدهای ایمپورت و نمونه‌سازی اپلیکیشن قرار دهید:

...
in_memory_datastore = {
   "COBOL" : {"name": "COBOL", "publication_year": 1960, "contribution": "record data"},
   "ALGOL" : {"name": "ALGOL", "publication_year": 1958, "contribution": "scoping and nested functions"},
   "APL" : {"name": "APL", "publication_year": 1962, "contribution": "array processing"},
}

این کد منبع in_memory_datastore با داده‌های سه زبان برنامه‌نویسی می‌سازد.

هر دیکشنری زبان برنامه‌نویسی شامل کلید‌هایی برای نام، سال انتشار و نحوه مشارکت آن در طراحی زبان‌های برنامه‌نویسی مدرن است.

اندپوینت list را با استفاده از دستور زیر بسازید. زیر دیکشنری in-memory datastore، اندپوینت list داده‌های زبان‌های برنامه‌نویسی را دریافت و در قالب یک فایل JSON نمایش می‌دهد.

...
@app.get('/programming_languages')
def list_programming_languages():
   return {"programming_languages":list(in_memory_datastore.values())}

درخواست‌ها با استفاده از دستور GET به آدرس programming_languages/ ارسال می‌کنند. این درخواست باید بدون پارامتر ارسال شود. در پاسخ درخواست نیز یک آبجکت JSON با کلید programming_languages ارسال می‌شود. این کلید رکوردها را نشان می‌دهد و در قالب یک آرایه نماش داده می‌شود.

اپلیکیشن را اجرا کنید تا داده بازگشت داده‌شده توسط اندپوینت list را ببینید. به دایرکتوری که اپ را در آن ذخیره کرده‌اید بروید و دستور زیر را اجرا کنید:

 export FLASK_APP=prog_lang_app.py
  flask run

مرورگر خودتان را باز کنید و به آدرس http://127.0.0.1:5000 بروید تا اپلیکیشن را به صورت لوکال روی سیستم خودتان اجرا کنید.

اگر به آدرس http://127.0.0.1:5000/programming_languages بروید، می‌توانید آبجکت JSON که حاوی داده‌های پایگاه داده است را ببینید.

ساخت اندپیونت Detail در فلسک

گام بعدی اضافه کردن یک اندپوینت برای دریافت جزییات داده یک زبان برنامه‌نویسی خاص است. اندپوینت details دارای یک متغیر با نام programming_language_id است. این متغیر به شما اجازه می‌دهد تا یک آیتم خاص در پایگاه داده دسترسی داشته باشید. Id اشاره شده در این متغیر، شماره ایندکس متغیر در مخزن زبان برنامه‌نویسی است. 

با اضافه کردن کد زیر به فایل prog_lang_app.py آن را به‌روزرسانی کنید:

...
@app.route('/programming_languages/<programming_language_name>')
def get_programming_language(programming_language_name):
   return in_memory_datastore[programming_language_name]

اپلیکیشن را اجرا کرده و به آدرس http://127.0.0.1:5000/programming_languages/COBOL بروید. شما باید خروجی مشابه زیر را ببینید:

{"contribution":"record data","name":"COBOL","publication_year":1960}

اضافه کردن فیلتر به اندپوینت List

دیکشنری in_memory_datastore در فایل prog_lang_app.py را با استفاده کد زیر به‌روزرسانی کنید:

...
in_memory_datastore = {
   "COBOL": {"name": "COBOL", "publication_year": 1960, "contribution": "record data"},
   "ALGOL": {"name": "ALGOL", "publication_year": 1958, "contribution": "scoping and nested functions"},
   "APL": {"name": "APL", "publication_year": 1962, "contribution": "array processing"},
   "BASIC": {"name": "BASIC", "publication_year": 1964, "contribution": "runtime interpretation, office tooling"},
   "PL": {"name": "PL", "publication_year": 1966, "contribution": "constants, function overloading, pointers"},
   "SIMULA67": {"name": "SIMULA67", "publication_year": 1967,
                "contribution": "class/object split, subclassing, protected attributes"},
   "Pascal": {"name": "Pascal", "publication_year": 1970,
              "contribution": "modern unary, binary, and assignment operator syntax expectations"},
   "CLU": {"name": "CLU", "publication_year": 1975,
           "contribution": "iterators, abstract data types, generics, checked exceptions"},
}
...

حالا شما می‌توانید کدی را که به کلاینت‌ها اجازه می‌دهد تا بر روی پارامتر publication_year فیلتر ایجاد کند، اضافه کنید:

from flask import Flask, request
app = Flask(__name__)
...

باید فانکشن ()list_programming_languages را به شکل زیر تغییر دهید:

...
@app.get('/programming_languages')
def list_programming_languages():
   before_year = request.args.get('before_year') or '30000'
   after_year = request.args.get('after_year') or '0'
   qualifying_data = list(
       filter(
           lambda pl: int(before_year) > pl['publication_year'] > int(after_year),
           in_memory_datastore.values()
       )
   )

   return {"programming_languages": qualifying_data}

حالا کلاینت‌ها می‌توانند با استفاده از دو پارامتر before_year و after_year زبان‌های برنامه‌نویسی را فیلتر کنند.

ساخت اندپوینت Create

تا به اینجای کار اندپوینت‌ها برای درخواست‌های GET نوشته شده‌اند، اما اندپوینت Create منتظر درخواست POST می‌ماند. بدنه این درخواست شمال تمام اطلاعات لازم برای ایجاد و اضافه کردن یک داده جدید است.

برای ساخت این اندپوینت، فایل prog_lang_app.py را به صورت زیر تغییر دهید:

...
@app.route('/programming_languages', methods=['GET', 'POST'])
def programming_languages_route():
   if request.method == 'GET':
       return list_programming_languages()
   elif request.method == "POST":
       return create_programming_language(request.get_json(force=True))

متد create_programming_language را زیر متد list_programming_languages اضافه کنید: 

...
def create_programming_language(new_lang):
   language_name = new_lang['name']
   in_memory_datastore[language_name] = new_lang
   return new_lang

حالا از دستور cURL برای ساخت یک زبان برنامه‌نویسی جدید استفاده کنید:

curl -X POST http://127.0.0.1:5000/programming_languages
      -H 'Content-Type: application/json'
      -d '{"name": "Java", "publication_year": 1995, "contribution": "Object-oriented programming language."}'

سپس با وارد کردن دستور زیر، یک درخواست GET ارسال کنید:

http://127.0.0.1:5000/programming_languages

  curl http://127.0.0.1:5000/programming_languages

مشاهده می‌کنید که داده مربوط به زبان برنامه‌نویسی جاوا در قالب JSON ارسال می‌شود.

ساخت اندپوینت Update

دستور PUT برای به‌روزرسانی داده استفاده می‌شود. برای ساخت این اندپیونت، در فایل prog_lang_app.py فانکشن‌های app.route@ و ()get_programming_language را حذف و به جای آن‌ها کد زیر را وارد کنید:

...
@app.route('/programming_languages/<programming_language_name>', methods=['GET', 'PUT'])
def programming_language_route(programming_language_name):
   if request.method == 'GET':
       return get_programming_language(programming_language_name)
   elif request.method == "PUT":
       return update_programming_language(programming_language_name, request.get_json(force=True))

حالا فانکشن ()update_programming_language را زیر فانکشن ()get_programming_language وارد کنید:

...
def update_programming_language(lang_name, new_lang_attributes):
   lang_getting_update = in_memory_datastore[lang_name]
   lang_getting_update.update(new_lang_attributes)
   return lang_getting_update

برای تست اندپوینت جدید خودتان، یک درخواست به‌روزرسانی را ارسال کنید:

curl -X PUT http://127.0.0.1:5000/programming_languages/Java
      -H 'Content-Type: application/json'
      -d '{"contribution": "The JVM"}'

حالا با ارسال یک درخواست GET به اندپوینت list، می توانید داده به‌روزشده را ببینید:

  curl http://127.0.0.1:5000/programming_languages

ساخت اندپوینت Delete

ساختار اندپوینت Delete مشابه اندپوینت Update است. برای اضافه کردن این اندپوینت، فایل prog_lang_app.py را به شکل زیر تغییر دهید:

...
@app.route('/programming_languages/<programming_language_name>', methods=['GET', 'PUT', 'DELETE'])
def programming_language_route(programming_language_name):
   if request.method == 'GET':
       return get_programming_language(programming_language_name)
   elif request.method == "PUT":
       return update_programming_language(programming_language_name, request.get_json(force=True))
   elif request.method == "DELETE":
       return delete_programming_language(programming_language_name)

حالا فانکشن ()delete_programming_language را زیر فانکشن ()update_programming_language اضافه کنید:

...
def delete_programming_language(lang_name):
   deleting_lang = in_memory_datastore[lang_name]
   del in_memory_datastore[lang_name]
   return deleting_lang

برای تست این اندپوینت، می‌توانید درخواست زیر را ارسال کنید:

  curl -X DELETE http://127.0.0.1:5000/programming_languages/COBOL


کد نهایی

در آخر فایل نهایی باید به شکل زیر باشد:

from flask import Flask, request

app = Flask(__name__)

in_memory_datastore = {
   "COBOL": {"name": "COBOL", "publication_year": 1960, "contribution": "record data"},
   "ALGOL": {"name": "ALGOL", "publication_year": 1958, "contribution": "scoping and nested functions"},
   "APL": {"name": "APL", "publication_year": 1962, "contribution": "array processing"},
   "BASIC": {"name": "BASIC", "publication_year": 1964, "contribution": "runtime interpretation, office tooling"},
   "PL": {"name": "PL", "publication_year": 1966, "contribution": "constants, function overloading, pointers"},
   "SIMULA67": {"name": "SIMULA67", "publication_year": 1967,
                "contribution": "class/object split, subclassing, protected attributes"},
   "Pascal": {"name": "Pascal", "publication_year": 1970,
              "contribution": "modern unary, binary, and assignment operator syntax expectations"},
   "CLU": {"name": "CLU", "publication_year": 1975,
           "contribution": "iterators, abstract data types, generics, checked exceptions"},
}

@app.route('/programming_languages', methods=['GET', 'POST'])
def programming_languages_route():
   if request.method == 'GET':
       return list_programming_languages()
   elif request.method == "POST":
       return create_programming_language(request.get_json(force=True))

def list_programming_languages():
   before_year = request.args.get('before_year') or '30000'
   after_year = request.args.get('after_year') or '0'
   qualifying_data = list(
       filter(
           lambda pl: int(before_year) > pl['publication_year'] > int(after_year),
           in_memory_datastore.values()
       )
   )

   return {"programming_languages": qualifying_data}

def create_programming_language(new_lang):
   language_name = new_lang['name']
   in_memory_datastore[language_name] = new_lang
   return new_lang

@app.route('/programming_languages/<programming_language_name>', methods=['GET', 'PUT', 'DELETE'])
def programming_language_route(programming_language_name):
   if request.method == 'GET':
       return get_programming_language(programming_language_name)
   elif request.method == "PUT":
       return update_programming_language(programming_language_name, request.get_json(force=True))
   elif request.method == "DELETE":
       return delete_programming_language(programming_language_name)

def get_programming_language(programming_language_name):
   return in_memory_datastore[programming_language_name]

def update_programming_language(lang_name, new_lang_attributes):
   lang_getting_update = in_memory_datastore[lang_name]
   lang_getting_update.update(new_lang_attributes)
   return lang_getting_update

def delete_programming_language(lang_name):
   deleting_lang = in_memory_datastore[lang_name]
   del in_memory_datastore[lang_name]
   return deleting_lang

اگر تمام درخوا‌ست‌های شما با موفقیت ارسال شده و پاسخ آن را دریافت می‌کنید، تبریک می‌گوییم، شما موفق به ساخت API با Flask شده‌اید.