ساخت یک API ساده با Flask: گام به گام

فهرست مطالب

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

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

مقدمه‌ای بر APIها و چرایی استفاده از Flask

API (Application Programming Interface) مجموعه‌ای از تعاریف و پروتکل‌هاست که به برنامه‌های نرم‌افزاری اجازه می‌دهد تا با یکدیگر تعامل داشته باشند. تصور کنید یک رستوران دارید؛ منو، روش سفارش‌گیری و قوانین مربوط به آن، API رستوران شماست. مشتریان (برنامه‌های دیگر) از طریق این API با شما (سرویس شما) ارتباط برقرار کرده و غذا (داده یا سرویس) دریافت می‌کنند. در واقع، APIها راهی استاندارد برای ارائه قابلیت‌های یک سیستم به سیستم‌های دیگر هستند، بدون اینکه نیاز باشد جزئیات داخلی پیاده‌سازی آن سیستم را بدانید.

انواع APIها و تمرکز بر RESTful

APIها در انواع مختلفی مانند SOAP، GraphQL، و REST وجود دارند. در این مقاله، تمرکز ما بر APIهای RESTful است. REST (Representational State Transfer) یک سبک معماری برای سیستم‌های توزیع شده است که بر اساس پروتکل HTTP عمل می‌کند و از روش‌های استاندارد HTTP (GET, POST, PUT, DELETE) برای انجام عملیات بر روی منابع استفاده می‌کند. سادگی، مقیاس‌پذیری و Stateless بودن (بدون حالت بودن) از ویژگی‌های کلیدی REST است که آن را به انتخابی محبوب برای APIهای وب مدرن تبدیل کرده است.

چرا Flask برای ساخت API؟

Flask یک میکرو-فریمورک وب برای پایتون است. عبارت “میکرو” به این معنی نیست که قابلیت‌های کمتری دارد، بلکه به این معناست که هسته آن بسیار سبک و حداقل‌گرا است و بسیاری از قابلیت‌ها را از طریق اکستنشن‌ها (extensions) فراهم می‌کند. این رویکرد چندین مزیت کلیدی برای ساخت API دارد:

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

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

آماده‌سازی محیط توسعه: نصب Python و Flask

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

نصب Python

اگر پایتون را قبلاً نصب نکرده‌اید، می‌توانید آن را از وب‌سایت رسمی Python (python.org) دانلود و نصب کنید. توصیه می‌شود از آخرین نسخه پایدار پایتون 3 (مثلاً 3.9 به بالا) استفاده کنید. هنگام نصب، اطمینان حاصل کنید که گزینه “Add Python to PATH” را تیک بزنید تا بتوانید به راحتی از خط فرمان به پایتون دسترسی داشته باشید.

ایجاد محیط مجازی (Virtual Environment)

محیط‌های مجازی بهترین روش برای مدیریت وابستگی‌های پروژه‌های پایتون هستند. آن‌ها به شما اجازه می‌دهند تا برای هر پروژه یک محیط ایزوله داشته باشید و از تداخل پکیج‌ها بین پروژه‌های مختلف جلوگیری کنید. برای ایجاد یک محیط مجازی، مراحل زیر را دنبال کنید:

  1. یک پوشه برای پروژه خود ایجاد کنید و وارد آن شوید:

    
    mkdir flask_book_api
    cd flask_book_api
    
  2. یک محیط مجازی ایجاد کنید. در پایتون 3، ماژول `venv` به صورت پیش‌فرض وجود دارد:

    
    python -m venv venv
    

    این دستور یک پوشه به نام `venv` در دایرکتوری پروژه شما ایجاد می‌کند که شامل یک نسخه ایزوله از پایتون و `pip` (مدیر پکیج پایتون) است.

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

    • ویندوز (Command Prompt):

      
      venv\Scripts\activate
      
    • ویندوز (PowerShell):

      
      .\venv\Scripts\Activate.ps1
      
    • macOS / Linux:

      
      source venv/bin/activate
      

    پس از فعال‌سازی، نام محیط مجازی ((venv)) در ابتدای خط فرمان شما نمایش داده می‌شود که نشان‌دهنده فعال بودن آن است.

نصب Flask و Flask-SQLAlchemy

حالا که محیط مجازی فعال است، می‌توانیم Flask و اکستنشن مورد نیاز برای کار با پایگاه داده، یعنی Flask-SQLAlchemy را نصب کنیم. Flask-SQLAlchemy یک انتگرال‌سازی بین Flask و SQLAlchemy (یک ORM قدرتمند پایتون) فراهم می‌کند.


pip install Flask Flask-SQLAlchemy

پس از اتمام نصب، می‌توانید با دستور `pip freeze` لیست پکیج‌های نصب شده در محیط مجازی خود را مشاهده کنید تا از نصب صحیح Flask و Flask-SQLAlchemy اطمینان حاصل کنید.


pip freeze

اکنون محیط توسعه شما آماده است و می‌توانیم به سراغ طراحی معماری و شروع به کدنویسی برویم.

فلسفه RESTful و طراحی Endpoints

برای ساخت یک API قوی و قابل نگهداری، درک اصول RESTful و نحوه صحیح طراحی Endpoints (نقطه‌های دسترسی) بسیار حیاتی است. یک API RESTful خوب، شهودی، قابل پیش‌بینی و استاندارد است. در این بخش، به بررسی این اصول و طراحی Endpoints برای API مدیریت کتاب‌ها می‌پردازیم.

اصول کلیدی RESTful

شش اصل کلیدی برای یک API RESTful وجود دارد:

  1. Client-Server: جداسازی نگرانی‌ها بین کلاینت (فرانت‌اند) و سرور (بک‌اند). کلاینت مسئول تجربه کاربری و سرور مسئول منطق کسب‌وکار و ذخیره‌سازی داده است.
  2. Stateless (بدون حالت): هر درخواست از کلاینت به سرور باید حاوی تمام اطلاعات لازم برای پردازش درخواست باشد. سرور نباید هیچ اطلاعاتی از درخواست‌های قبلی کلاینت را ذخیره کند.
  3. Cacheable (قابل ذخیره‌سازی): پاسخ‌های سرور باید قابلیت ذخیره‌سازی توسط کلاینت‌ها یا سرورهای میانی را داشته باشند تا عملکرد را بهبود بخشند.
  4. Layered System (سیستم لایه‌ای): کلاینت نیازی ندارد بداند که آیا به طور مستقیم به سرور نهایی متصل است یا یک واسطه (مانند لود بالانسر، پروکسی) بین آن‌ها وجود دارد.
  5. Uniform Interface (رابط یکنواخت): این اصل حیاتی‌ترین اصل REST است و شامل چهار زیرشاخه است:
    • Identification of resources (شناسایی منابع): هر منبع (مانند یک کتاب، یک کاربر) باید یک URI (Uniform Resource Identifier) یکتا داشته باشد.
    • Manipulation of resources through representations (دستکاری منابع از طریق نمایش): کلاینت‌ها با نمایش منابع (مثلاً JSON یا XML) تعامل دارند. تغییر در این نمایش، منجر به تغییر در منبع می‌شود.
    • Self-descriptive messages (پیام‌های خود-توصیفی): هر پیام باید شامل اطلاعات کافی برای کلاینت باشد تا نحوه پردازش آن را بداند (مانند نوع محتوا در هدر HTTP).
    • Hypermedia as the Engine of Application State (HATEOAS): سرور باید لینک‌هایی را در پاسخ‌های خود ارائه دهد که کلاینت را برای انجام عملیات بعدی راهنمایی کند. (این مورد برای یک API ساده، ممکن است در مراحل اولیه پیاده‌سازی نشود.)
  6. Code on Demand (کد در صورت نیاز – اختیاری): سرور می‌تواند کد قابل اجرا (مانند جاوا اسکریپت) را برای افزایش قابلیت‌های کلاینت ارسال کند.

طراحی Endpoints برای مدیریت کتاب‌ها

برای API مدیریت کتاب‌های خود، منابع اصلی ما “کتاب‌ها” (books) خواهند بود. ما عملیات CRUD را بر روی این منابع پیاده‌سازی می‌کنیم. در اینجا نحوه نگاشت عملیات CRUD به متدهای HTTP و URIها آورده شده است:

عملیات متد HTTP URI (آدرس) توضیحات
ایجاد (Create) POST /books افزودن یک کتاب جدید به مجموعه. بدنه درخواست حاوی اطلاعات کتاب است.
خواندن (Read All) GET /books بازیابی لیست تمام کتاب‌ها.
خواندن (Read One) GET /books/<book_id> بازیابی اطلاعات یک کتاب خاص بر اساس شناسه آن.
به‌روزرسانی (Update) PUT /books/<book_id> به‌روزرسانی اطلاعات یک کتاب خاص. بدنه درخواست حاوی اطلاعات جدید کتاب است.
حذف (Delete) DELETE /books/<book_id> حذف یک کتاب خاص از مجموعه.

این طراحی، یک ساختار منطقی و استاندارد برای API ما فراهم می‌کند که هم برای توسعه‌دهندگان کلاینت قابل درک است و هم از اصول RESTful پیروی می‌کند. در بخش‌های بعدی، این Endpoints را با استفاده از Flask پیاده‌سازی خواهیم کرد.

پیاده‌سازی مدل داده و اتصال به پایگاه داده با Flask-SQLAlchemy

برای ذخیره دائمی داده‌های کتاب‌ها، نیاز به یک پایگاه داده داریم. در این پروژه، از SQLite به عنوان پایگاه داده و از Flask-SQLAlchemy به عنوان ORM (Object-Relational Mapper) استفاده خواهیم کرد. Flask-SQLAlchemy به ما امکان می‌دهد تا با اشیاء پایتون (مدل‌ها) کار کنیم و از نوشتن کوئری‌های SQL به صورت مستقیم جلوگیری کنیم، که توسعه را سریع‌تر و کد را خواناتر می‌کند.

پیکربندی Flask-SQLAlchemy

ابتدا، فایل اصلی اپلیکیشن Flask خود را ایجاد می‌کنیم. فرض کنید نام فایل `app.py` باشد. در این فایل، Flask و Flask-SQLAlchemy را پیکربندی می‌کنیم.


# app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

توضیحات کد:

  • `from flask import Flask`: کلاس `Flask` را ایمپورت می‌کنیم که هسته اپلیکیشن ماست.
  • `from flask_sqlalchemy import SQLAlchemy`: کلاس `SQLAlchemy` را برای انتگرال‌سازی با پایگاه داده ایمپورت می‌کنیم.
  • `app = Flask(__name__)`: یک نمونه از اپلیکیشن Flask ایجاد می‌کنیم. `__name__` به Flask کمک می‌کند تا ریشه‌ی اپلیکیشن را پیدا کند.
  • `app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘sqlite:///site.db’`: این خط، URI پایگاه داده ما را تنظیم می‌کند. `sqlite:///site.db` به این معنی است که از پایگاه داده SQLite استفاده می‌کنیم و فایل پایگاه داده `site.db` در کنار فایل `app.py` ایجاد خواهد شد.
  • `app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’] = False`: این تنظیم برای غیرفعال کردن ردیابی تغییرات آبجکت‌ها در SQLAlchemy است که می‌تواند حافظه زیادی مصرف کند و معمولاً برای APIها نیازی به آن نیست.
  • `db = SQLAlchemy(app)`: یک نمونه از `SQLAlchemy` ایجاد می‌کنیم و آن را به اپلیکیشن Flask خود متصل می‌کنیم. این شیء `db` برای تعریف مدل‌ها و انجام عملیات پایگاه داده استفاده خواهد شد.

تعریف مدل Book

حالا، مدل `Book` را تعریف می‌کنیم که نمایانگر جدول `books` در پایگاه داده ما خواهد بود. هر ستون در جدول به عنوان یک ویژگی (attribute) در کلاس `Book` تعریف می‌شود.


# app.py (ادامه)

class Book(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    author = db.Column(db.String(100), nullable=False)
    isbn = db.Column(db.String(20), unique=True, nullable=False)
    publication_year = db.Column(db.Integer, nullable=True)

    def __repr__(self):
        return f"<Book {self.title} by {self.author}>"

    def to_dict(self):
        return {
            'id': self.id,
            'title': self.title,
            'author': self.author,
            'isbn': self.isbn,
            'publication_year': self.publication_year
        }

توضیحات مدل:

  • `class Book(db.Model):`: کلاس `Book` از `db.Model` ارث‌بری می‌کند که نشان می‌دهد یک مدل SQLAlchemy است.
  • `id = db.Column(db.Integer, primary_key=True)`: ستون `id` را تعریف می‌کند که از نوع عدد صحیح است و کلید اصلی (Primary Key) جدول است. `primary_key=True` به صورت خودکار آن را Auto-increment می‌کند.
  • `title = db.Column(db.String(100), nullable=False)`: ستون `title` از نوع رشته با حداکثر طول 100 کاراکتر است و نمی‌تواند `NULL` باشد.
  • `author = db.Column(db.String(100), nullable=False)`: مشابه `title` برای نام نویسنده.
  • `isbn = db.Column(db.String(20), unique=True, nullable=False)`: ستون `isbn` (شماره استاندارد بین‌المللی کتاب) از نوع رشته با طول 20 کاراکتر است، باید یکتا باشد (`unique=True`) و نمی‌تواند `NULL` باشد.
  • `publication_year = db.Column(db.Integer, nullable=True)`: ستون سال انتشار که از نوع عدد صحیح است و می‌تواند `NULL` باشد.
  • `def __repr__(self)`: یک متد خاص پایتون است که نمایش رشته‌ای از شیء `Book` را برمی‌گرداند. این برای دیباگینگ مفید است.
  • `def to_dict(self)`: این متد برای تبدیل شیء `Book` به یک دیکشنری پایتون استفاده می‌شود که به راحتی می‌تواند به JSON تبدیل شود. این یک الگوی رایج در APIها است.

ایجاد پایگاه داده

پس از تعریف مدل، باید پایگاه داده را ایجاد کنیم. این کار را می‌توانیم با استفاده از یک اسکریپت پایتون ساده یا با استفاده از Flask Shell انجام دهیم.

روش اول: با استفاده از اسکریپت در فایل app.py


# app.py (ادامه)

if __name__ == '__main__':
    with app.app_context():
        db.create_all() # این خط تمام جداول تعریف شده را ایجاد می کند
    app.run(debug=True)

هنگامی که برای اولین بار `app.py` را اجرا کنید، `db.create_all()` جداول را بر اساس مدل `Book` در فایل `site.db` ایجاد خواهد کرد.

روش دوم: با استفاده از Flask Shell (توصیه شده برای توسعه)

  1. متغیر محیطی `FLASK_APP` را تنظیم کنید تا Flask بداند کدام فایل را اجرا کند:

    
    # macOS / Linux
    export FLASK_APP=app.py
    
    # Windows (Command Prompt)
    set FLASK_APP=app.py
    
    # Windows (PowerShell)
    $env:FLASK_APP="app.py"
    
  2. Flask Shell را اجرا کنید:

    
    flask shell
    
  3. در Flask Shell، دستورات زیر را وارد کنید تا جداول ایجاد شوند:

    
    >>> from app import db
    >>> with app.app_context(): # برای کار با db در خارج از context درخواست نیاز است
    ...     db.create_all()
    ...     exit()
    

پس از اجرای `db.create_all()`، فایل `site.db` در دایرکتوری پروژه شما ایجاد خواهد شد و شامل جدول `book` با ستون‌های تعریف شده خواهد بود. با این کار، زیرساخت پایگاه داده ما آماده است و می‌توانیم به سراغ پیاده‌سازی Endpoints برویم.

ساخت Endpoints برای عملیات CRUD

در این بخش، Endpoints RESTful را که قبلاً طراحی کرده بودیم، با استفاده از Flask و مدل `Book` پیاده‌سازی می‌کنیم. ما برای هر عملیات CRUD (Create, Read, Update, Delete) یک route (مسیر) و یک تابع ویو (view function) در Flask ایجاد خواهیم کرد.

Imports و تنظیمات اولیه

مطمئن شوید که `request` و `jsonify` را نیز از Flask ایمپورت کرده‌اید. `request` برای دسترسی به داده‌های درخواست (مانند JSON در بدنه درخواست) و `jsonify` برای تبدیل دیکشنری‌های پایتون به پاسخ‌های JSON استاندارد استفاده می‌شود.


# app.py (ادامه از بخش قبل)

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
# ... (app, db, Book definition from previous section)

Endpoint برای دریافت تمام کتاب‌ها (GET /books)

این Endpoint لیست تمام کتاب‌های موجود در پایگاه داده را برمی‌گرداند.


@app.route('/books', methods=['GET'])
def get_books():
    books = Book.query.all()
    return jsonify([book.to_dict() for book in books])

توضیحات:

  • `@app.route(‘/books’, methods=[‘GET’])`: این دکوراتور (decorator) تابع `get_books` را به مسیر `/books` با متد HTTP `GET` متصل می‌کند.
  • `books = Book.query.all()`: تمام رکوردها را از جدول `book` با استفاده از `Book.query.all()` بازیابی می‌کند.
  • `jsonify([book.to_dict() for book in books])`: لیست اشیاء `Book` را به لیست دیکشنری‌ها تبدیل کرده و سپس با `jsonify` به یک پاسخ JSON تبدیل می‌کند.

Endpoint برای دریافت یک کتاب خاص (GET /books/<id>)

این Endpoint یک کتاب خاص را بر اساس `id` آن بازیابی می‌کند.


@app.route('/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
    book = Book.query.get_or_404(book_id) # get_or_404 به جای find by id میگرد و اگه نباشه 404 میده
    return jsonify(book.to_dict())

توضیحات:

  • `<int:book_id>`: این قسمت نشان‌دهنده یک پارامتر دینامیک در URL است که باید یک عدد صحیح باشد و به عنوان آرگومان `book_id` به تابع `get_book` منتقل می‌شود.
  • `book = Book.query.get_or_404(book_id)`: با استفاده از `get_or_404`، کتاب را بر اساس `id` آن پیدا می‌کند. اگر کتابی با آن `id` یافت نشود، Flask به طور خودکار یک خطای 404 (Not Found) را برمی‌گرداند.

Endpoint برای افزودن کتاب جدید (POST /books)

این Endpoint یک کتاب جدید را به پایگاه داده اضافه می‌کند. اطلاعات کتاب از بدنه درخواست به فرمت JSON دریافت می‌شود.


@app.route('/books', methods=['POST'])
def add_book():
    data = request.get_json()
    if not data:
        return jsonify({"message": "Invalid JSON data"}), 400
    
    # Optional: Basic validation
    if not all(key in data for key in ['title', 'author', 'isbn']):
        return jsonify({"message": "Missing required fields: title, author, isbn"}), 400
    
    # Check for duplicate ISBN
    existing_book = Book.query.filter_by(isbn=data['isbn']).first()
    if existing_book:
        return jsonify({"message": "Book with this ISBN already exists"}), 409 # 409 Conflict

    new_book = Book(
        title=data['title'],
        author=data['author'],
        isbn=data['isbn'],
        publication_year=data.get('publication_year') # .get برای فیلدهای اختیاری
    )
    db.session.add(new_book)
    db.session.commit()
    return jsonify(new_book.to_dict()), 201 # 201 Created

توضیحات:

  • `data = request.get_json()`: داده‌های JSON ارسالی در بدنه درخواست را دریافت می‌کند و به یک دیکشنری پایتون تبدیل می‌کند.
  • `if not data:`: بررسی می‌کند که آیا داده JSON معتبری دریافت شده است یا خیر.
  • `if not all(…)`: یک اعتبارسنجی اولیه برای فیلدهای اجباری (`title`, `author`, `isbn`).
  • `existing_book = Book.query.filter_by(isbn=data[‘isbn’]).first()`: قبل از افزودن کتاب جدید، بررسی می‌کند که آیا کتابی با همین ISBN قبلاً وجود دارد یا خیر تا از تکرار جلوگیری کند.
  • `new_book = Book(…)`: یک شیء `Book` جدید با داده‌های دریافتی ایجاد می‌کند.
  • `db.session.add(new_book)`: شیء `new_book` را به نشست (session) پایگاه داده اضافه می‌کند.
  • `db.session.commit()`: تغییرات را به پایگاه داده اعمال (commit) می‌کند.
  • `return jsonify(new_book.to_dict()), 201`: اطلاعات کتاب جدید را به همراه کد وضعیت 201 (Created) برمی‌گرداند.

Endpoint برای به‌روزرسانی کتاب (PUT /books/<id>)

این Endpoint اطلاعات یک کتاب موجود را بر اساس `id` آن به‌روزرسانی می‌کند.


@app.route('/books/<int:book_id>', methods=['PUT'])
def update_book(book_id):
    book = Book.query.get_or_404(book_id)
    data = request.get_json()
    if not data:
        return jsonify({"message": "Invalid JSON data"}), 400

    # Optional: More robust validation could be added here
    # Example: Check if ISBN is being changed to an existing one (excluding self)

    if 'title' in data:
        book.title = data['title']
    if 'author' in data:
        book.author = data['author']
    if 'isbn' in data:
        # Check if new ISBN already exists for another book
        if data['isbn'] != book.isbn: # Only check if ISBN is actually changing
            existing_book_with_isbn = Book.query.filter_by(isbn=data['isbn']).first()
            if existing_book_with_isbn and existing_book_with_isbn.id != book_id:
                return jsonify({"message": "Book with this ISBN already exists"}), 409
        book.isbn = data['isbn']
    if 'publication_year' in data:
        book.publication_year = data['publication_year']
    
    db.session.commit()
    return jsonify(book.to_dict())

توضیحات:

  • `book = Book.query.get_or_404(book_id)`: کتاب مورد نظر را پیدا می‌کند.
  • `data = request.get_json()`: داده‌های به‌روزرسانی را از بدنه درخواست دریافت می‌کند.
  • چون `PUT` معمولاً برای جایگزینی کامل منبع استفاده می‌شود، ما فیلدها را بر اساس حضور آن‌ها در `data` به‌روزرسانی می‌کنیم. اگر از `PATCH` استفاده می‌کردید، این رویکرد مناسب‌تر است. برای `PUT` می‌توانستید تمام فیلدها را اجباری کنید.
  • `db.session.commit()`: تغییرات را به پایگاه داده اعمال می‌کند.
  • `return jsonify(book.to_dict())`: اطلاعات به‌روز شده کتاب را برمی‌گرداند.

Endpoint برای حذف کتاب (DELETE /books/<id>)

این Endpoint یک کتاب خاص را بر اساس `id` آن از پایگاه داده حذف می‌کند.


@app.route('/books/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
    book = Book.query.get_or_404(book_id)
    db.session.delete(book)
    db.session.commit()
    return jsonify({"message": "Book deleted successfully"}), 200 # 200 OK

توضیحات:

  • `book = Book.query.get_or_404(book_id)`: کتاب مورد نظر را پیدا می‌کند.
  • `db.session.delete(book)`: شیء `book` را برای حذف از پایگاه داده علامت‌گذاری می‌کند.
  • `db.session.commit()`: حذف را نهایی می‌کند.
  • `return jsonify(…)`, 200: یک پیام تأیید و کد وضعیت 200 (OK) برمی‌گرداند.

با پیاده‌سازی این Endpoints، ما اکنون یک API کامل با عملیات CRUD برای مدیریت کتاب‌ها داریم. در بخش بعدی، به مباحث مهم مدیریت خطاها و اعتبارسنجی ورودی‌ها می‌پردازیم تا API ما قوی‌تر و کاربرپسندتر شود.

مدیریت خطاها و اعتبارسنجی ورودی‌ها

یک API قوی و قابل اطمینان باید خطاهای احتمالی را به خوبی مدیریت کرده و پاسخ‌های معنی‌داری به کلاینت‌ها برگرداند. همچنین، اعتبارسنجی ورودی‌ها برای اطمینان از صحت و امنیت داده‌ها بسیار حیاتی است. در این بخش، به چگونگی بهبود مدیریت خطا و اعتبارسنجی در API Flask خود می‌پردازیم.

مدیریت خطاهای HTTP استاندارد

Flask به طور پیش‌فرض، برخی از خطاهای HTTP رایج مانند 404 (Not Found) را به خوبی مدیریت می‌کند (همانطور که در `get_or_404` دیدیم). با این حال، می‌توانیم برای نمایش بهتر و یکپارچه‌تر خطاهای HTTP، هندلرهای سفارشی تعریف کنیم.


# app.py (ادامه)

@app.errorhandler(404)
def not_found_error(error):
    return jsonify({"message": "Resource not found"}), 404

@app.errorhandler(405)
def method_not_allowed_error(error):
    return jsonify({"message": "Method not allowed for this resource"}), 405

@app.errorhandler(500)
def internal_server_error(error):
    # Log the error for debugging purposes
    app.logger.error('Server Error: %s', (error))
    return jsonify({"message": "Internal server error"}), 500

توضیحات:

  • `@app.errorhandler(code)`: این دکوراتور به Flask می‌گوید که تابع زیر را برای رسیدگی به خطای HTTP با کد مشخص فراخوانی کند.
  • این توابع، یک پاسخ JSON با پیام خطا و کد وضعیت HTTP مناسب برمی‌گردانند، که به کلاینت کمک می‌کند تا مشکل را تشخیص دهد.
  • برای خطای 500، توصیه می‌شود که خطا را لاگ (log) کنید تا بتوانید در محیط پروداکشن مشکلات را پیگیری کنید. برای این کار، نیاز به راه‌اندازی سیستم لاگینگ Flask دارید (که خارج از حیطه این مقاله است اما نکته مهمی برای پروژه‌های واقعی است).

اعتبارسنجی ورودی‌ها (Input Validation)

اعتبارسنجی ورودی‌ها به معنی بررسی اینکه آیا داده‌های دریافتی از کلاینت مطابق با انتظارات ما هستند یا خیر. این کار از داده‌های نامعتبر، ناتمام یا مخرب که می‌توانند به پایگاه داده آسیب برسانند یا باعث رفتارهای غیرمنتظره شوند، جلوگیری می‌کند.

در مثال `add_book` و `update_book`، ما یک اعتبارسنجی اولیه را برای فیلدهای اجباری انجام دادیم. اما برای یک API تخصصی‌تر، نیاز به اعتبارسنجی قوی‌تری داریم. کتابخانه‌هایی مانند `Flask-WTF` (با `wtforms`) یا `Marshmallow` برای این منظور بسیار مفید هستند.

برای این آموزش، ما از `Marshmallow` استفاده می‌کنیم که یک کتابخانه عالی برای serialization/deserialization و اعتبارسنجی داده‌ها در پایتون است. ابتدا آن را نصب کنید:


pip install marshmallow

تعریف Schema با Marshmallow

یک `Schema` با `Marshmallow`، ساختار و قواعد اعتبارسنجی داده‌های ورودی و خروجی را تعریف می‌کند.


# app.py (ادامه)

from marshmallow import Schema, fields, validate
# ... (app, db, Book definition)

class BookSchema(Schema):
    id = fields.Int(dump_only=True) # dump_only یعنی فقط برای خروجی serialization است، نه ورودی
    title = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    author = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    isbn = fields.Str(required=True, validate=validate.Length(min=10, max=20))
    publication_year = fields.Int(required=False, validate=validate.Range(min=1000, max=2100))

توضیحات `BookSchema`:

  • `id = fields.Int(dump_only=True)`: `id` فقط برای خروجی است و نباید در ورودی درخواست `POST`/`PUT` ارسال شود.
  • `title = fields.Str(required=True, validate=validate.Length(min=1, max=100))`: `title` یک رشته اجباری است و طول آن باید بین 1 تا 100 کاراکتر باشد.
  • `isbn = fields.Str(required=True, validate=validate.Length(min=10, max=20))`: `isbn` یک رشته اجباری با طول مشخص است.
  • `publication_year = fields.Int(required=False, validate=validate.Range(min=1000, max=2100))`: `publication_year` اختیاری است و باید یک عدد صحیح در محدوده مشخص باشد.

استفاده از Schema برای اعتبارسنجی در Endpoints

حالا، Endpoints `add_book` و `update_book` را با استفاده از `BookSchema` بازنویسی می‌کنیم.


# app.py (ادامه)
# ...

book_schema = BookSchema()
books_schema = BookSchema(many=True) # برای لیست کتاب ها

# Endpoint برای افزودن کتاب جدید (POST /books) - با Marshmallow
@app.route('/books', methods=['POST'])
def add_book():
    try:
        # Load and validate request data using the schema
        data = book_schema.load(request.get_json())
    except Exception as e: # ValidationError as err for specific marshmallow error
        return jsonify({"message": "Validation Error", "errors": str(e)}), 400

    # Check for duplicate ISBN
    existing_book = Book.query.filter_by(isbn=data['isbn']).first()
    if existing_book:
        return jsonify({"message": "Book with this ISBN already exists"}), 409 # 409 Conflict

    new_book = Book(
        title=data['title'],
        author=data['author'],
        isbn=data['isbn'],
        publication_year=data.get('publication_year')
    )
    db.session.add(new_book)
    db.session.commit()
    return book_schema.jsonify(new_book), 201 # Use schema for serialization too

# Endpoint برای به‌روزرسانی کتاب (PUT /books/) - با Marshmallow
@app.route('/books/<int:book_id>', methods=['PUT'])
def update_book(book_id):
    book = Book.query.get_or_404(book_id)
    try:
        data = book_schema.load(request.get_json(), partial=True) # partial=True برای به‌روزرسانی جزئی
    except Exception as e: # ValidationError as err
        return jsonify({"message": "Validation Error", "errors": str(e)}), 400

    # Check if new ISBN already exists for another book
    if 'isbn' in data and data['isbn'] != book.isbn:
        existing_book_with_isbn = Book.query.filter_by(isbn=data['isbn']).first()
        if existing_book_with_isbn and existing_book_with_isbn.id != book_id:
            return jsonify({"message": "Book with this ISBN already exists"}), 409

    for key, value in data.items():
        setattr(book, key, value)
    
    db.session.commit()
    return book_schema.jsonify(book) # Use schema for serialization

# Endpoint برای دریافت تمام کتاب‌ها (GET /books) - با Marshmallow (بهبود یافته)
@app.route('/books', methods=['GET'])
def get_books():
    books = Book.query.all()
    return books_schema.jsonify(books)

# Endpoint برای دریافت یک کتاب خاص (GET /books/) - با Marshmallow (بهبود یافته)
@app.route('/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
    book = Book.query.get_or_404(book_id)
    return book_schema.jsonify(book)

توضیحات تغییرات:

  • `book_schema = BookSchema()`: یک نمونه از Schema برای یک شیء کتاب ایجاد می‌کنیم.
  • `books_schema = BookSchema(many=True)`: یک نمونه از Schema برای لیستی از اشیاء کتاب ایجاد می‌کنیم.
  • `book_schema.load(request.get_json())`: داده‌های JSON را بارگذاری و اعتبارسنجی می‌کند. اگر داده‌ها نامعتبر باشند، یک `ValidationError` (یا یک `Exception` کلی‌تر) ایجاد می‌شود که می‌توانیم آن را با `try-except` مدیریت کنیم و یک خطای 400 با جزئیات اعتبارسنجی برگردانیم.
  • `partial=True` در `update_book`: این به Marshmallow می‌گوید که همه فیلدها برای اعتبارسنجی لازم نیستند، که برای عملیات `PUT`/`PATCH` بسیار مفید است وقتی فقط بخشی از منابع به‌روزرسانی می‌شود.
  • `book_schema.jsonify(new_book)`: به جای `jsonify(new_book.to_dict())`، از `book_schema.jsonify()` استفاده می‌کنیم که به طور خودکار شیء `Book` را به JSON مناسب (بر اساس Schema) تبدیل می‌کند.

با این تغییرات، API ما نه تنها خطاهای استاندارد HTTP را بهتر مدیریت می‌کند، بلکه ورودی‌ها را نیز به طور قوی‌تری اعتبارسنجی می‌کند که منجر به یک API پایدارتر و امن‌تر می‌شود. این رویکرد، برای جامعه تخصصی که به دنبال ساخت APIهای قوی هستند، بسیار توصیه می‌شود.

اجرا، تست و ملاحظات استقرار

پس از پیاده‌سازی Endpoints و اعتبارسنجی، زمان آن رسیده که API خود را اجرا کرده، آن را تست کنیم و سپس نگاهی کوتاه به ملاحظات استقرار (Deployment) در محیط واقعی داشته باشیم.

اجرای API Flask

برای اجرای اپلیکیشن Flask در حالت توسعه، مطمئن شوید که محیط مجازی فعال است و سپس متغیر `FLASK_APP` را تنظیم کرده‌اید (همانطور که قبلاً توضیح داده شد). سپس می‌توانید اپلیکیشن را اجرا کنید:


# اطمینان از فعال بودن محیط مجازی
# اگر فعال نیست: source venv/bin/activate (macOS/Linux) یا venv\Scripts\activate (Windows)

# تنظیم متغیر محیطی (اگر قبلاً انجام نشده است)
# export FLASK_APP=app.py (macOS/Linux)
# set FLASK_APP=app.py (Windows)

flask run

به طور پیش‌فرض، Flask بر روی `http://127.0.0.1:5000` (localhost پورت 5000) اجرا می‌شود. می‌توانید با باز کردن این آدرس در مرورگر خود، یک صفحه “Not Found” (به دلیل عدم وجود مسیر اصلی ‘/’) مشاهده کنید، اما این نشان‌دهنده فعال بودن سرور است.

تست API

برای تست API، نیاز به ابزاری داریم که بتواند درخواست‌های HTTP با متدهای مختلف (GET, POST, PUT, DELETE) ارسال کرده و پاسخ‌های JSON را دریافت و نمایش دهد. ابزارهای محبوبی برای این منظور عبارتند از:

  • Postman: یک ابزار گرافیکی بسیار محبوب و قدرتمند برای تست API.
  • Insomnia: یک جایگزین عالی برای Postman با رابط کاربری زیبا.
  • curl: یک ابزار خط فرمان که در اکثر سیستم‌عامل‌ها موجود است و برای تست سریع مفید است.
  • Python `requests` library: برای تست برنامه‌نویسی‌شده (automated testing) از داخل کد پایتون.

در اینجا چند مثال با `curl` برای تست Endpoints که ساخته‌ایم آورده شده است:

1. دریافت لیست تمام کتاب‌ها (GET /books)


curl http://127.0.0.1:5000/books

پاسخ مورد انتظار (اگر هیچ کتابی نباشد):


[]

2. افزودن یک کتاب جدید (POST /books)


curl -X POST -H "Content-Type: application/json" -d '{
    "title": "The Hitchhiker\"s Guide to the Galaxy",
    "author": "Douglas Adams",
    "isbn": "9780345391803",
    "publication_year": 1979
}' http://127.0.0.1:5000/books

پاسخ مورد انتظار (همراه با `id` اختصاص داده شده):


{
  "author": "Douglas Adams",
  "id": 1,
  "isbn": "9780345391803",
  "publication_year": 1979,
  "title": "The Hitchhiker's Guide to the Galaxy"
}

3. دریافت یک کتاب خاص (GET /books/<id>)


curl http://127.0.0.1:5000/books/1

پاسخ مورد انتظار (کتاب با `id=1`):


{
  "author": "Douglas Adams",
  "id": 1,
  "isbn": "9780345391803",
  "publication_year": 1979,
  "title": "The Hitchhiker's Guide to the Galaxy"
}

4. به‌روزرسانی یک کتاب (PUT /books/<id>)


curl -X PUT -H "Content-Type: application/json" -d '{
    "title": "The Ultimate Hitchhiker\"s Guide",
    "publication_year": 1986
}' http://127.0.0.1:5000/books/1

پاسخ مورد انتظار (کتاب به‌روز شده):


{
  "author": "Douglas Adams",
  "id": 1,
  "isbn": "9780345391803",
  "publication_year": 1986,
  "title": "The Ultimate Hitchhiker's Guide"
}

5. حذف یک کتاب (DELETE /books/<id>)


curl -X DELETE http://127.0.0.1:5000/books/1

پاسخ مورد انتظار:


{
  "message": "Book deleted successfully"
}

با تست کردن هر Endpoint، می‌توانید از صحت عملکرد API خود اطمینان حاصل کنید و در صورت بروز خطا، به سرعت آن را رفع کنید.

ملاحظات استقرار (Deployment)

Flask یک وب سرور داخلی برای توسعه دارد که برای تست محلی مناسب است، اما هرگز نباید از آن در محیط پروداکشن استفاده شود. برای استقرار یک اپلیکیشن Flask در محیط واقعی، نیاز به یک WSGI سرور (مانند Gunicorn یا uWSGI) و یک وب سرور پروکسی معکوس (مانند Nginx یا Apache) دارید.

مراحل کلی استقرار:

  1. انتخاب WSGI سرور: Gunicorn یک انتخاب محبوب برای Flask است. آن را نصب کرده (`pip install gunicorn`) و اپلیکیشن خود را با آن اجرا کنید: `gunicorn -w 4 app:app` (که 4 worker process را برای `app` در فایل `app.py` اجرا می‌کند).
  2. وب سرور پروکسی معکوس: Nginx یا Apache را نصب و پیکربندی کنید تا درخواست‌ها را به Gunicorn فوروارد کند. این کار مزایایی مانند بارگذاری متعادل (load balancing)، کشینگ (caching)، سرویس‌دهی فایل‌های استاتیک و SSL را فراهم می‌کند.
  3. پایگاه داده: برای محیط پروداکشن، SQLite معمولاً کافی نیست و باید از پایگاه داده‌های قوی‌تر مانند PostgreSQL یا MySQL استفاده کنید. پیکربندی `SQLALCHEMY_DATABASE_URI` را متناسب با پایگاه داده جدید تغییر دهید.
  4. مدیریت محیط: از ابزارهایی مانند Docker برای کانتینری‌سازی اپلیکیشن خود استفاده کنید تا محیط توسعه و پروداکشن سازگار باشند.
  5. مدیریت لاگ‌ها: سیستم لاگینگ Flask را پیکربندی کنید تا خطاها و اطلاعات را به صورت مناسب در فایل‌ها یا سیستم‌های مرکزی لاگ‌گیری کند.
  6. متغیرهای محیطی: اطلاعات حساس مانند کلیدهای API، رمزهای عبور پایگاه داده و… را هرگز به صورت Hardcode در کد خود قرار ندهید. از متغیرهای محیطی برای مدیریت آن‌ها استفاده کنید (مانند `os.environ`).

استقرار یک موضوع گسترده است و نیاز به دانش خاص خود دارد، اما درک این ملاحظات اولیه برای توسعه‌دهندگان تخصصی ضروری است تا بتوانند APIهای خود را از فاز توسعه به فاز پروداکشن منتقل کنند.

افزایش امنیت و احراز هویت

در حالی که API ما عملکردی است، برای استفاده در دنیای واقعی، امنیت و احراز هویت (Authentication) از اهمیت بالایی برخوردارند. هیچ کس نمی‌خواهد داده‌های خصوصی‌اش توسط افراد غیرمجاز قابل دسترسی باشد. در این بخش، به طور خلاصه به چند روش برای افزایش امنیت API و پیاده‌سازی احراز هویت اشاره می‌کنیم.

پروتکل HTTPS

اولین گام و مهم‌ترین گام برای امنیت هر API وب، استفاده از HTTPS است. HTTPS ترافیک بین کلاینت و سرور را رمزگذاری می‌کند و از حملاتی مانند شنود (eavesdropping) و دستکاری (tampering) جلوگیری می‌کند. در محیط پروداکشن، باید یک گواهی SSL/TLS برای دامین خود دریافت کرده و وب سرور خود (مانند Nginx) را برای استفاده از HTTPS پیکربندی کنید.

احراز هویت (Authentication)

احراز هویت به فرآیند تأیید هویت کاربر یا سرویس متقاضی دسترسی به منابع API اشاره دارد. روش‌های مختلفی برای احراز هویت در APIهای RESTful وجود دارد:

  1. Basic Authentication: یک روش ساده است که نام کاربری و رمز عبور را به صورت Base64-encoded در هدر `Authorization` ارسال می‌کند. این روش به دلیل ناامنی ذاتی (Base64 رمزگذاری نیست، فقط کدگذاری است) توصیه نمی‌شود، مگر اینکه همراه با HTTPS استفاده شود.
  2. API Keys: کلاینت‌ها یک توکن یا کلید منحصربه‌فرد را در هدر یا پارامتر کوئری ارسال می‌کنند. سرور این کلید را با کلیدهای معتبر مقایسه می‌کند. این روش ساده است اما مدیریت کلیدها (ایجاد، ابطال) می‌تواند چالش‌برانگیز باشد.
  3. OAuth 2.0: یک چارچوب استاندارد برای اعطای دسترسی محدود (delegated authorization) به منابع محافظت‌شده است. OAuth 2.0 به طور مستقیم برای احراز هویت نیست، بلکه برای اعطای مجوز (Authorization) استفاده می‌شود، اما معمولاً در کنار یک روش احراز هویت (مانند JWT) به کار می‌رود. پیاده‌سازی آن پیچیده‌تر است اما برای سناریوهای پیچیده و سرویس‌های شخص ثالث ضروری است.
  4. JSON Web Tokens (JWT): یک استاندارد فشرده و خود-توصیفی برای انتقال امن اطلاعات بین طرفین به عنوان یک شیء JSON است. JWTها به دلیل Stateless بودنشان (مناسب برای REST) و مقیاس‌پذیری‌شان محبوب هستند. Flask دارای اکستنشن‌هایی مانند `Flask-JWT-Extended` برای پیاده‌سازی آسان JWT است.

برای API مدیریت کتاب‌ها، استفاده از JWT یک انتخاب عالی است، به خصوص برای یک جامعه تخصصی. بیایید یک پیاده‌سازی بسیار ساده از JWT را با `Flask-JWT-Extended` بررسی کنیم.

پیاده‌سازی JWT با Flask-JWT-Extended (مثال ساده)

ابتدا، `Flask-JWT-Extended` را نصب کنید:


pip install Flask-JWT-Extended

سپس، اپلیکیشن خود را پیکربندی و Endpoints احراز هویت و محافظت شده را اضافه کنید:


# app.py (ادامه)

from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
import datetime

# ... (app, db, Book, BookSchema, etc. from previous sections)

app.config["JWT_SECRET_KEY"] = "super-secret-key"  # این را در محیط پروداکشن به یک رشته قوی و متغیر محیطی تغییر دهید
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = datetime.timedelta(hours=1) # توکن ها پس از 1 ساعت منقضی می شوند
jwt = JWTManager(app)

# یک مدل ساده برای User برای احراز هویت
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False) # در واقعیت باید هش شود

    def __repr__(self):
        return f"<User {self.username}>"

# ایجاد جدول کاربران در پایگاه داده (یک بار)
# with app.app_context():
#     db.create_all() # اطمینان حاصل کنید که User هم ایجاد شود.

# Endpoint برای لاگین و دریافت توکن JWT
@app.route("/login", methods=["POST"])
def login():
    username = request.json.get("username", None)
    password = request.json.get("password", None)

    # در واقعیت، باید رمز عبور هش شده را مقایسه کنید
    # و کاربر را از پایگاه داده بازیابی کنید.
    # برای مثال ساده، یک کاربر تست را هاردکد می کنیم.
    if username != "test" or password != "test":
        return jsonify({"msg": "Bad username or password"}), 401
    
    # identity می تواند شناسه کاربر باشد
    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)

# Endpoint محافظت شده
@app.route("/protected", methods=["GET"])
@jwt_required() # این دکوراتور مسیر را محافظت می کند
def protected():
    # دسترسی به هویت کاربر از توکن
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

# اعمال احراز هویت به Endpoints CRUD کتاب ها
# شما باید هر endpoint که نیاز به احراز هویت دارد را با @jwt_required() تزئین کنید.
# مثال:
# @app.route('/books', methods=['POST'])
# @jwt_required()
# def add_book():
#     # ... منطق افزودن کتاب ...

توضیحات JWT:

  • `app.config[“JWT_SECRET_KEY”]`: یک کلید مخفی برای امضای توکن‌ها. این باید یک رشته قوی و محرمانه باشد و در محیط پروداکشن از طریق متغیرهای محیطی مدیریت شود.
  • `JWTManager(app)`: Flask-JWT-Extended را به اپلیکیشن Flask متصل می‌کند.
  • `@app.route(“/login”, methods=[“POST”])`: یک مسیر برای لاگین کاربر.
  • `create_access_token(identity=username)`: یک توکن دسترسی JWT ایجاد می‌کند. `identity` می‌تواند شناسه یکتا برای کاربر باشد (مانند `user.id` از پایگاه داده).
  • `@jwt_required()`: این دکوراتور روی هر تابعی قرار می‌گیرد که نیاز به احراز هویت دارد. اگر توکن معتبری در هدر `Authorization: Bearer ` ارسال نشود، درخواست با خطای 401 (Unauthorized) رد می‌شود.
  • `get_jwt_identity()`: در داخل یک تابع محافظت شده، این تابع هویت کاربر را که در زمان ایجاد توکن تنظیم شده است، برمی‌گرداند.

برای تست این بخش، ابتدا به `POST /login` یک درخواست با نام کاربری و رمز عبور (مثلاً `{“username”: “test”, “password”: “test”}`) ارسال می‌کنید تا یک `access_token` دریافت کنید. سپس این `access_token` را در هدر `Authorization: Bearer ` برای دسترسی به مسیرهای محافظت شده (مانند `/protected` یا Endpoints CRUD کتاب‌ها) ارسال می‌کنید.

مدیریت خطاها در JWT

Flask-JWT-Extended نیز دارای توابع `errorhandler` خاص خود برای مدیریت خطاهای مربوط به JWT است، مانند توکن‌های منقضی شده یا توکن‌های نامعتبر، که می‌توانید آن‌ها را سفارشی‌سازی کنید تا پاسخ‌های معنی‌داری به کلاینت‌ها برگردانید.

کنترل دسترسی (Authorization)

پس از احراز هویت، نیاز به کنترل دسترسی (Authorization) داریم که مشخص می‌کند آیا کاربر احراز هویت شده، اجازه دسترسی به یک منبع خاص یا انجام یک عملیات خاص را دارد یا خیر. این معمولاً شامل بررسی نقش‌های کاربر (role-based access control – RBAC) یا مجوزهای خاص (permission-based access control) است. مثلاً، فقط مدیران اجازه حذف کتاب را داشته باشند. این منطق معمولاً پس از `jwt_required()` در داخل تابع ویو پیاده‌سازی می‌شود یا از اکستنشن‌هایی مانند `Flask-Principal` استفاده می‌شود.

با پیاده‌سازی HTTPS، احراز هویت (مانند JWT) و کنترل دسترسی، API شما به طور قابل توجهی امن‌تر خواهد شد و آماده مقابله با تهدیدات رایج امنیتی خواهد بود.

نتیجه‌گیری و گام‌های بعدی

در این مقاله جامع، ما گام به گام فرآیند ساخت یک API ساده با Flask را از ابتدا تا انتها طی کردیم. از مقدمه‌ای بر APIها و چرایی انتخاب Flask شروع کردیم، محیط توسعه را آماده کردیم، اصول RESTful را برای طراحی Endpoints به کار بردیم، مدل داده‌ای خود را با Flask-SQLAlchemy تعریف کرده و به پایگاه داده متصل شدیم. سپس، Endpoints کامل CRUD را برای مدیریت کتاب‌ها پیاده‌سازی کردیم و با استفاده از Marshmallow، اعتبارسنجی قوی ورودی‌ها و مدیریت خطاهای استاندارد را به API خود اضافه نمودیم.

همچنین، به چگونگی اجرای و تست API با ابزارهایی مانند `curl` پرداختیم و ملاحظات مهمی را در خصوص استقرار در محیط پروداکشن، از جمله استفاده از WSGI سرورها و وب سرورهای پروکسی معکوس، بررسی کردیم. در نهایت، موضوع حیاتی امنیت و احراز هویت را معرفی کرده و یک پیاده‌سازی ساده از JSON Web Tokens (JWT) با استفاده از `Flask-JWT-Extended` را نمایش دادیم.

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

  • پیاده‌سازی Pagination: برای Endpoints که لیست‌های بزرگ داده را برمی‌گردانند (مانند `/books` در صورت وجود هزاران کتاب)، پیاده‌سازی pagination (صفحه‌بندی) برای مدیریت پاسخ‌های کارآمدتر ضروری است.
  • فیلترینگ و جستجو: افزودن قابلیت‌های فیلترینگ بر اساس ویژگی‌های مختلف (مثلاً `GET /books?author=Douglas Adams`) و جستجو برای یافتن منابع خاص.
  • تست‌های واحد و ادغام (Unit and Integration Tests): نوشتن تست‌های خودکار برای اطمینان از صحت عملکرد API در طول زمان و با تغییرات کد. Flask و پایتون اکوسیستم‌های قوی برای تست دارند (مانند `pytest`).
  • داکیومنت‌سازی API: استفاده از ابزارهایی مانند `Flask-RESTX` یا `Connexion` (با OpenAPI/Swagger) برای تولید خودکار و تعاملی مستندات API. این کار به توسعه‌دهندگان کلاینت کمک می‌کند تا به راحتی از API شما استفاده کنند.
  • Rate Limiting: محدود کردن تعداد درخواست‌هایی که یک کلاینت می‌تواند در یک بازه زمانی مشخص ارسال کند تا از سوء استفاده یا حملات DoS جلوگیری شود. اکستنشن‌هایی مانند `Flask-Limiter` می‌توانند در این زمینه کمک کنند.
  • Log Management: راه‌اندازی یک سیستم لاگینگ جامع برای رصد و عیب‌یابی اپلیکیشن در محیط پروداکشن.
  • Dockerization: کانتینری‌سازی اپلیکیشن Flask با Docker برای اطمینان از محیط‌های توسعه، تست و پروداکشن سازگار.
  • Advanced ORM Usage: کاوش عمیق‌تر در قابلیت‌های SQLAlchemy مانند روابط بین مدل‌ها (مثلاً یک کتاب می‌تواند چندین نویسنده داشته باشد یا یک نویسنده چندین کتاب)، جوین‌ها و کوئری‌های پیچیده‌تر.

ساخت API یک مهارت بنیادین در توسعه نرم‌افزار مدرن است و Flask ابزاری قدرتمند و منعطف برای انجام این کار فراهم می‌کند. با تمرین و کاوش بیشتر در اکوسیستم Flask، می‌توانید APIهای بسیار پیچیده‌تر و با قابلیت‌های بیشتری را توسعه دهید. امیدواریم این راهنما به شما در شروع این مسیر کمک کرده باشد.

“تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”

قیمت اصلی 2.290.000 ریال بود.قیمت فعلی 1.590.000 ریال است.

"تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"

"با شرکت در این دوره جامع و کاربردی، به راحتی مهارت‌های برنامه‌نویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر می‌سازد تا به سرعت الگوریتم‌های پیچیده را درک کرده و اپلیکیشن‌های هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفه‌ای و امکان دانلود و تماشای آنلاین."

ویژگی‌های کلیدی:

بدون نیاز به تجربه قبلی برنامه‌نویسی

زیرنویس فارسی با ترجمه حرفه‌ای

۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان