آنچه در این مقاله میخوانید
ساخت API توسط Flask-RESTPlus، Flask و Swagger UI
۲۴ تیر ۱۳۹۹
هنگام کار بر روی پروژههای یادگیری ماشین، تصمیم گرفتم که یک برنامه کامل را توسعه دهم. این قضیه به توسعه APIها نیاز دارد که ما بتوانیم دادهها را وارد کنیم (post data) و از طرف دیگر پیشبینیها را دریافت کنیم (get data). در اینجاست که Flask و Flask-RESTPlus وارد صحنه میشوند.
Flask این قابلیت را به توسعهدهنده میدهد که از توابع پایتونی برای ایجاد APIها استفاده کند. Flask-RESTPlus یک افزونه برای Flask است که قابلیتهای بیشتر، نظیر ایجاد REST APIها را به Flask به میدهد. این افزونه نه تنها به ما اجازه میدهد که REST APIها را ایجاد کنیم، بلکه میتوان تمام APIها را به Swagger UI اضافه کرد.
در این مقاله، نحوه توسعه یک برنامه توسط Flask به همراه چندین API و مقداری دیتا را بررسی خواهیم کرد. تمام کدهایی که در مقاله میبینید در این ریپازیتوری در دسترس هستند.
در ادامه خواهید خواند:
- نصب
- استفادههای بیشتر
- جمع بندی

نصب
مراحل انجام این پروژه را با ایجاد یک محیط مجازی (virtual environment) برای کتابخانههای پایتون، با استفاده از دستور pipenv شروع میکنم. برای مشاهد تفاوت میان محیطهای مجازی، میتوانید به این مقاله مراجعه کنید. ابتدا Flask و Flask-RESTPlus را نصب میکنیم:
pipenv install flask
pipenv install flask-restplus
بههرحال اگر نخواهید از pipenv و یا محیطهای مجازی استفاده کنید، میتوانید به سادگی از pip استفاده کنید:
pip install flask
pip install flask-restplus
Import
پروژه را با واردکردن (import کردن) Flask از ماژول flask و Api و Resource از ماژول flask_restplus آغاز میکنم. از Api برای ساخت برنامه و از Resource به عنوان ورودی برای کلاسهایی که در داخل پروژه تعریف کردیم، استفاده میکنم:
from flask import Flask
from flask_restplus import Api, Resource
ساخت برنامه
برنامه flask را توسط تابع Flask، که نام آن را توسط __name__ تنظیم میکند، ایجاد کردم. در مرحله بعد از Api برای راهاندازی برنامه استفاده میکنم:
flask_app = Flask(__name__)
app = Api(app = flask_app)
name_space = app.namespace('main', description='Main APIs')
در اینجا namespace را ایجاد کردم. مفهوم و تصور کلی از آن بسیار ساده است. هرگاه APIهایی را در زیر یک namespace تعریف کنیم، آنها در زیر یک دستهبندی در Swagger UI قرار میگیرند (Swagger UI را در ادامه بررسی میکنیم). در تعریف namespace، متغیر اول، مسیر را تعریف میکند و متغیر دوم توضیحی اضافه برای آن قسمت را تعریف میکند.
در مثال بالا آدرس namespace ایجاد شده به این صورت است: http://127.0.0.1:5000/main و توضیحات آن در Swagger برابر با Main APIs است.
تعریف APIها
در مرحله قبلی یک namespace ایجاد کر دیم. از آنجایی که متغیری که namespace را در آن ایجاد کردیم، name_space نام دارد، برای ایجاد route در ادامه این namespace از @name_space.route("/") قبل ایجاد متدها استفاده میکنیم. در ادامه نیاز است که endpointها را در داخل این route ایجاد کنیم. متدهای داخل این route میتواند get() و یا post() و … باشد.
@name_space.route("/")
class MainClass(Resource):
def get(self):
return {
"status": "Got new data"
}
def post(self):
return {
"status": "Posted new data"
}
در این مثال، API از طریق مسیر http://127.0.0.1:5000/main در دسترس است. نام کلاس MainClass است که شامل دو متد get() و post() میشود. هرگاه درخواستی با متد GET به این API ارسال کنم، فیلدی بنام status با مقدار Got new data دریافت میکنم، زمان استفاده از متد POST، مقدار این فیلد برابر با Posted new data خواهد بود.
API چیست؟ معرفی کاربرد ها و انواع API
API چیست؟
اجرای برنامه
حالا که همه چیز آماده است، باید برنامه را که در فایلی به نام basic.py ذخیره کردهایم، توسط pipenv اجرا کنیم:
pipenv shell
FLASK_APP=basic.py flask run
اما اگر در مرحله اول از pip به جای pipenv استفاده کردید، از دستور زیر برای اجرای برنامه استفاده کنید:
FLASK_APP=basic.py flask run
Swagger UI
بهترین بخش Flask-RESTPlus این است که به صورت خودکار مستندات APIهایی که ایجاد کردیم ساخته میشود و در Swagger UI قابل مشاهده هستند. آدرس http://127.0.0.1:5000 را در مرورگر خود وارد کنید در نتیجه تمامی APIهایی که ایجاد کردهاید را مشاهده خواهید کرد.

هردوی APIهایی که ایجاد کردیم، در زیر namespace با نام main که توضیحات آن Main APIs است، قابل مشاهده است. میتوانیم هردو آنها را به همراه کارکردشان توسط دکمه Try it out امتحان کنیم.
آزمایش API
از curl برای ایجاد یک درخواست GET و POST در ترمینال استفاده میکنم:

به هنگام استفاده از دستور curl، ابتدا از واژه curl به همراه نوع متد درخواست بعد از سوییچ -X استفاده کنید. در انتها هم مقصد را مشخص کنید. با توجه به پاسخی که از curl دریافت کردیم، میفهمیم که دیتای درستی را از هر دو API، یعنی هم GET و هم POST دریافت شدهاست.
استفادههای بیشتر
موارد قابل استفاده بیشتری در رابطه با Flask و FlaskRESTPlus وجود دارد. بیایید آنها را عمیقتر بررسی کنیم تا بتوانیم آنها را بهتر متوجه شویم. کدی که در ادامه بررسی خواهیم کرد، تحت عنوان app.py در ریپازیتوری موجود است.
میتوانیم از متد POST برای ارسال دیتا و ذخیرهشان و از متد GET هم برای دریافت و مشاهده آنها بهره ببریم. بیایید در نظر بگیریم که پروژهای داریم که در آن اسامی اشخاص را ذخیره و مدیریت میکنیم. یک endpoint با متد GET ایجاد کردیم که میتوانیم توسط آن یک اسم را با استفاده id آن، دریافت کنیم، همچنین endpoint دیگری داریم که در آن از متد POST برای ذخیره هر اسم در مقابل یک id، استفاده میکنیم.
در اینجا، این مسیر را ایجاد کردهام: http//127.0.0.1:5000/names/<int:id> که هر بار مقدار id را در آن ارسال میکنیم. برای ذخیره سازی آنها، آبجکتی با نام list_of_names ایجاد کردهام که برای دریافت و ارسال دیتا مورد استفاده قرار خواهد گرفت.
اپلیکیشنهای Flask خود را با سرعت، امنیت و پایداری بالا اجرا کنید.
✅ پشتیبانی کامل از فریمورک Flask✅ دیپلوی آسان با Git یا CLI✅ منابع اختصاصی و عملکرد بهینه
خرید و راهاندازی هاست Flask
استفاده بیشتر از کتابخانهها
تا به اینجای کار Api، Flask و Resource را وارد کدمان کردهایم. در این بخش request را از ماژول flask وارد کدمان میکنیم، که به ما این امکان را میدهد یک درخواست را دریافت کنیم و بتوانیم از اطلاعات آن در قالبی مثل JSON استفاده کنیم. همچنین fields را از ماژول flask_restplus، برای تعریف انواع داده مختلف، مثل String وارد کد کردیم.
from flask import Flask, request
from flask_restplus import Api, Resource, fields
افزودن اطلاعات برنامه
همجنین میتوانیم اطلاعات بیشتری به برنامه flaskمان اضافه کنیم. این اطلاعات در برخی موارد بسیار مفید خواهند بود و حتی در Swagger UI هم نمایش داده خواهند شد.
flask_app = Flask(__name__)
app = Api(app = flask_app,
version = "1.0",
title = "Name Recorder",
description = "Manage names of various users of the application")
name_space = app.namespace('names', description='Manage names')
میتوانیم title، version و description برنامهمان را تعریف کنیم. همچنین نام تنها namespace را names مقدار دادیم. حالا سرتیتر در Swagger UI باید مانند تصویر زیر باشد:

تعریف مدلها
هرگاه بخواهیم اطلاعات را در قالب خاصی (JSON) ارسال و یا دریاف کنیم، از model استفاده میکنیم. ابتدا نام آن را مشخص میکنیم. در مرحله بعد اطلاعاتی که نیاز دارد، به همراه ویژگیهای آنها را تعریف میکنیم.
model = app.model('Name Model',
{'name': fields.String(required = True,
description="Name of the person",
help="Name cannot be blank.")})
نام این مدل را Name Model تنظیم کردیم که شامل یک پارامتر با نام name میشود که این فیلد، یک فیلد ضروری (required) است و نمیتواند خالی باشد، همچنین توضیحات و متن راهنما برای آن تنظیم کردیم. API زمانی که قصد استفاده از این مدل را داشته باشد، انتظار دریافت دیتا در قالب JSON با کلیدی به نام name را دارد.
list_of_names = {}
برای ذخیره تمامی اسمها، آنها را در list_of_names ذخیره میکنم.
تعریف APIها
@name_space.route("/<int:id>")
class MainClass(Resource):
@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, params={ 'id': 'Specify the Id associated with the person' })
def get(self, id):
try:
name = list_of_names[id]
return {
"status": "Person retrieved",
"name" : list_of_names[id]
}
except KeyError as e:
name_space.abort(500, e.__doc__, status = "Could not retrieve information", statusCode = "500")
except Exception as e:
name_space.abort(400, e.__doc__, status = "Could not retrieve information", statusCode = "400")
@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, params={ 'id': 'Specify the Id associated with the person' })
@app.expect(model)
def post(self, id):
try:
list_of_names[id] = request.json['name']
return {
"status": "New person added",
"name": list_of_names[id]
}
except KeyError as e:
name_space.abort(500, e.__doc__, status = "Could not save information", statusCode = "500")
except Exception as e:
name_space.abort(400, e.__doc__, status = "Could not save information", statusCode = "400")
بیایید کد بالا را به بخشهای کوچکتری تقسیم کنیم تا فهم آن آسانتر شود. ابتدا بخش POST را بررسی میکنیم، کارکرد بخش GET مشابه POST خواهد بود.
تعریف route و کلاس
@name_space.route("/<int:id>")
class MainClass(Resource):
از name_space که در بالاتر ایجاد کردیم، برای ایجاد route استفاده میکنیم. http://127.0.0.1:5000/main/<int:id> این آدرس انتظار دارد id، که از نوع عدد صحیح است را به عنوان پارامتر دریافت کند. نام کلاس MainClass است که Resource را به عنوان ورودی به آن دادهایم.
نحوه ایجاد یک REST API با Flask در سرور مجازی اوبونتو Ubuntu
ایجاد یک REST API با Flask در سرور مجازی اوبونتو
ایجاد مستندات برای API
@app.doc(responses={ 200: 'OK', 400: 'Invalid Argument', 500: 'Mapping Key Error' }, params={ 'id': 'Specify the Id associated with the person' })
استفاده از doc باعث میشود که بتوانیم مستنداتی برای APIمان در Swagger UI ایجاد کنیم. responses، انواع مختلفی از کدهای وضعیت مختلف HTTP را برای شرایط مختلف شامل میشود. برای هر کد وضعیت، توضیحاتی تعریف کردهایم که اطلاعات بیشتری به کاربر میدهد. params، پارامترهایی که انتظار دریافت آنها را داریم، مشخص میکند. API در درخواستی که از سمت کاربر دریافت میکند انتظار وجود id را در URL دارد و همچنین یک متن راهنما برای نمایش به کاربر تعیین کردهایم. تا به اینجای کار Swagger UI باید شبیه تصویر زیر باشد:

پارامترها در بخش بالایی تصویر نمایش داده شدهاند. تمام پاسخهایی که ممکن است به سمت کاربر برگردد، به همراه توضیحات آنها، در بخش پایینی تصویر قرار دارد.
تعریف متد
@app.expect(model)
def post(self, id):
try:
list_of_names[id] = request.json['name']
return {
"status": "New person added",
"name": list_of_names[id]
}
except KeyError as e:
name_space.abort(500, e.__doc__, status = "Could not save information", statusCode = "500")
except Exception as e:
name_space.abort(400, e.__doc__, status = "Could not save information", statusCode = "400")
حالا میتوانیم متد خودمان را تعریف کنیم. اما قبل از تعریف متد، @app.expect(model) را نوشتهایم که به این معناست API انتظار دریافت model را دارد. همچنین کدمان را در داخل بلوک try گذاشتهایم تا بتوانیم تمام خطاهای احتمالی را کنترل کنیم. request.json['name'] مقدار name دریافتی را برمیگرداند که درنتیجه میتوانیم آن را ذخیره کنیم همانطور که میتوانیم آن را در پاسخ به کاربر نیز ارسال کنیم. اگر کلید name وجود نداشته باشد، با خطای KeyError روبرو خواهیم شد و در پاسخ کاربر، کد وضعیت 500 را به همراه توضیحاتی که برای آن تعیین کردیم ارسال خواهیم کرد. در سایر خطاها کد وضعیت 400 را ارسال خواهیم کرد.
آزمایش برنامه
با دستور زیر برنامه را اجرا میکنیم:
FLASK_APP=app.py flask run
POST
ابتدا بعد از دریافت درخواست، مقدار name را از داخل آن بدست میآوریم و سپس آن را در مقابل یک id در list_of_names ذخیره میکنیم. همچنین وضعیت و نام شخص جدیدی که آن را ذخیره کردیم را نیز برمیگردانیم.


خطا در درخواست POST
فرض کنیم در موردی فراموش شده که به name مقداری اختصاص بدهیم. در این حالت با خطا مواجه خواهیم شد.


همانطور که مشاهده کردید، کلید name را در دیتای ارسالی خود به API مشخص نکردیم و با خطایی با کد 500 و پیام Mapping key not found. روبرو شدیم.
GET
در این متد تنها نیاز داریم که id نامی که میخواهیم مقدار آن را دریافت کنیم را در درخواست ارسال کنیم، در پاسخ اگر شخصی با چنین id وجود داشته باشد، کد وضعیت و نام آن شخص را دریافت خواهیم کرد.


خطا در درخواست GET
در نظر بگیرید که شخصی با id برابر 2 نداشته باشیم. اگر درخواستی برای دریافت اطلاعات شخص با id = 2 کنیم، با خطا روبرو خواهیم شد.


از آنجایی که چنین شخصی با id مشخص شده وجود نداشت، به خطا با کد 500 و پیام Mapping key not found. برخورد کردیم.
معرفی هاست رایگان Flask
هاست رایگان Flask
جمعبندی
در این مقاله با ساخت یک API ساده در فریمورک Flask آشنا شدیم و دیدیم که چطور میتوان با استفاده از Flask-RESTX، مستندسازی به سبک Swagger را نیز بهراحتی به پروژه اضافه کرد. این رویکرد نه تنها فرآیند توسعه API را سریعتر و ساختیافتهتر میکند، بلکه باعث میشود رابط کاربری خوانایی برای تست و بررسی endpointها در اختیار داشته باشیم.
ساختار ساده و ماژولار Flask به همراه مستندات تعاملی Swagger، گزینهای مناسب برای پروژههای کوچک تا متوسط فراهم میکند. البته در پروژههای واقعی معمولاً مواردی مثل احراز هویت، اتصال به دیتابیس واقعی، مدیریت خطاها و سطح دسترسیها هم باید در نظر گرفته شود.