Table of Contents
ایدهٔ کلی: API در وب چیست؟
در بخشهای قبلی فصل، با Flask و ساخت یک وبسرور ساده آشنا شدهاید؛ همین وبسرور میتواند علاوه بر ارسال صفحهٔ HTML، دادهٔ خام (مثلاً JSON) برگرداند. این نوع سرویسها را معمولاً «وب API» مینامیم.
در این بخش روی API های بسیار ساده تمرکز میکنیم؛ یعنی:
- فقط چند آدرس (route) خیلی کوچک
- بدون احراز هویت، بدون دیتابیس پیچیده
- فقط برای برگرداندن/گرفتن داده به صورت ماشینخوان (معمولاً JSON)
در عمل این API ها میتوانند:
- توسط جاوااسکریپت در مرورگر مصرف شوند
- توسط اسکریپتهای پایتونِ دیگر مصرف شوند
- توسط ابزارهایی مثل
curlو Postman تست شوند
JSON و پاسخهای API
متداولترین قالب داده برای API های وب، JSON است. از نظر ظاهری شبیه دیکشنریهای پایتون است:
- کلید–مقدار
- لیستها
- اعداد، رشتهها، مقدارهای بولی
در Flask، برای برگرداندن JSON معمولاً از jsonify استفاده میکنیم.
مثال یک API خیلی ساده که یک پیام را به صورت JSON برمیگرداند:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/api/hello")
def hello_api():
data = {
"message": "سلام از API!",
"language": "fa",
"version": 1
}
return jsonify(data)
if __name__ == "__main__":
app.run(debug=True)
وقتی در مرورگر به آدرس http://127.0.0.1:5000/api/hello بروید، یک JSON میبینید.
نکتهها:
jsonifyدیکشنری یا لیست پایتون را به JSON تبدیل میکند.- هِدر مناسب
Content-Typeرا هم خودکار تنظیم میکند.
API مبتنی بر لیست دادهٔ ساده (در حافظه)
برای تمرین اولیه، به جای پایگاه داده، میتوانیم از یک لیست در حافظه استفاده کنیم. فرض کنید میخواهیم یک API خیلی ساده برای «لیست کارها (todo)» بسازیم.
در اینجا فقط:
- یک آدرس برای گرفتن همهٔ کارها (GET)
- یک آدرس برای افزودن کار جدید (POST)
را پیادهسازی میکنیم.
from flask import Flask, jsonify, request
app = Flask(__name__)
# دادهٔ نمونه در حافظه
todos = [
{"id": 1, "title": "یادگیری پایتون", "done": False},
{"id": 2, "title": "پیادهروی", "done": True},
]
# گرفتن لیست همهٔ کارها
@app.route("/api/todos", methods=["GET"])
def get_todos():
return jsonify(todos)
# افزودن یک کار جدید
@app.route("/api/todos", methods=["POST"])
def add_todo():
# دادهٔ JSON فرستاده شده توسط کاربر
data = request.get_json()
# سادهترین حالت: فقط عنوان را میگیریم
title = data.get("title", "بدون عنوان")
# ایجاد یک id ساده
new_id = len(todos) + 1
new_todo = {
"id": new_id,
"title": title,
"done": False
}
todos.append(new_todo)
# برگرداندن کار جدید با کد وضعیت 201 (ساخته شد)
return jsonify(new_todo), 201
if __name__ == "__main__":
app.run(debug=True)چند نکتهٔ مهم:
- در
@app.routeباmethods=["GET"]وmethods=["POST"]مشخص میکنیم هر آدرس چه نوع درخواستهایی را قبول میکند. - برای خواندن JSON ارسالی از سمت کاربر، از
request.get_json()استفاده میکنیم. - در پاسخ
POST، معمول است شیء ساختهشده را همراه با کد وضعیت201برگردانیم.
این API را در عمل میتوانید با ابزارهایی مثل Postman یا curl تست کنید.
مفهوم endpoint و روش (method)
برای کار با API ها باید دو چیز را از هم تفکیک کنید:
- آدرس (URL)
- مثلاً
/api/todosیا/api/hello - روش درخواست (HTTP Method)
GET: گرفتن دادهPOST: ایجاد دادهٔ جدید- (در API های پیشرفته معمولاً
PUT,PATCH,DELETEهم داریم، اما اینجا فقط ازGETوPOSTاستفاده میکنیم.)
ترکیب (آدرس + روش) را عموماً یک endpoint مینامند.
مثلاً:
GET /api/todos→ گرفتن لیست کارهاPOST /api/todos→ افزودن کار جدید
برگرداندن کد وضعیت (Status Code)
API فقط داده برنمیگرداند؛ یک کد وضعیت هم برمیگرداند که به برنامهٔ مصرفکننده میگوید چه اتفاقی افتاده است.
چند کد متداول که در API های ساده کافی هستند:
200→ موفقیت (موفقیت درGET)201→ ایجاد شد (موفقیت درPOSTکه چیزی ساخته است)400→ درخواست نادرست (مثلاً داده ناقص است)404→ پیدا نشد (مثلاً آیتمی با آنidوجود ندارد)
در Flask میتوانیم همراه jsonify یک عدد وضعیت برگردانیم:
return jsonify({"error": "عنوان لازم است"}), 400مثال سادهٔ واکنش به ورودی ناقص در API Todo:
@app.route("/api/todos", methods=["POST"])
def add_todo():
data = request.get_json() or {}
title = data.get("title")
if not title:
return jsonify({"error": "فیلد 'title' الزامی است."}), 400
new_id = len(todos) + 1
new_todo = {"id": new_id, "title": title, "done": False}
todos.append(new_todo)
return jsonify(new_todo), 201گرفتن یک مورد خاص توسط مسیر پویا
همانطور که در بخش «مسیرها» دیدهاید، میتوانیم از قسمتهای پویا در URL استفاده کنیم. برای API نیز همین کار را میکنیم تا یک آیتم خاص را بگیریم.
مثال: گرفتن یک کار بر اساس id:
@app.route("/api/todos/<int:todo_id>", methods=["GET"])
def get_todo(todo_id):
# جستجوی آیتم با id خواستهشده
for todo in todos:
if todo["id"] == todo_id:
return jsonify(todo)
# اگر پیدا نشد
return jsonify({"error": "آیتم پیدا نشد"}), 404نکته:
<int:todo_id>یعنی این قسمت از آدرس یک عدد صحیح (int) است.- در صورت پیدا نشدن، JSON حاوی پیام خطا + کد وضعیت 404 برمیگردانیم.
نمونهٔ یک API بسیار ساده و کاملتر
در این مثال، یک API کوچک برای مدیریت «یادداشتها» داریم که چند قابلیت پایه دارد:
GET /api/notes→ گرفتن همهٔ یادداشتهاPOST /api/notes→ ایجاد یادداشت جدیدGET /api/notes/<id>→ گرفتن یک یادداشتDELETE /api/notes/<id>→ حذف یادداشت (به صورت خیلی ساده)
from flask import Flask, jsonify, request
app = Flask(__name__)
notes = [
{"id": 1, "text": "اولین یادداشت من"},
{"id": 2, "text": "یادداشت دوم"},
]
# گرفتن همهٔ یادداشتها
@app.route("/api/notes", methods=["GET"])
def get_notes():
return jsonify(notes)
# ایجاد یادداشت جدید
@app.route("/api/notes", methods=["POST"])
def create_note():
data = request.get_json() or {}
text = data.get("text")
if not text:
return jsonify({"error": "فیلد 'text' الزامی است."}), 400
new_id = (notes[-1]["id"] + 1) if notes else 1
note = {"id": new_id, "text": text}
notes.append(note)
return jsonify(note), 201
# گرفتن یک یادداشت بر اساس id
@app.route("/api/notes/<int:note_id>", methods=["GET"])
def get_note(note_id):
for note in notes:
if note["id"] == note_id:
return jsonify(note)
return jsonify({"error": "یادداشت پیدا نشد"}), 404
# حذف یک یادداشت
@app.route("/api/notes/<int:note_id>", methods=["DELETE"])
def delete_note(note_id):
for i, note in enumerate(notes):
if note["id"] == note_id:
deleted = notes.pop(i)
return jsonify({"deleted": deleted})
return jsonify({"error": "یادداشتی با این id وجود ندارد"}), 404
if __name__ == "__main__":
app.run(debug=True)چند نکته برای فهم بهتر:
- اینجا از
DELETEهم استفاده کردیم تا ببینید چطور یک روش دیگر را به route اضافه میکنیم. - هنوز از پایگاه داده استفاده نمیکنیم؛ همهچیز در حافظه (لیست پایتون) است. اگر برنامه را ریاستارت کنید، دادهها از بین میروند؛ برای تمرین ساده اشکالی ندارد.
- پیامهای خطا را هم به صورت JSON برمیگردانیم، نه HTML.
نحوهٔ تست یک API ساده
در حد مقدماتی، چند راه برای تست این API ها وجود دارد:
- مرورگر (فقط برای GET)
- آدرس
http://127.0.0.1:5000/api/notesرا در مرورگر باز کنید و JSON را ببینید. - ابزار Postman یا Insomnia
- میتوانید روش (GET/POST/DELETE)، آدرس، و JSON ارسالی را مشخص کنید.
- استفاده از curl در ترمینال (اختیاری)
- مثال ارسال
POSTبرای ایجاد یادداشت:
curl -X POST http://127.0.0.1:5000/api/notes \
-H "Content-Type: application/json" \
-d "{\"text\": \"یک یادداشت جدید\"}"در این مرحله هدف فقط درک کلی است؛ لازم نیست همهٔ ابزارها را بهطور حرفهای بلد باشید.
جمعبندی ایدههای اصلی برای API های بسیار ساده
در حدی که در این فصل نیاز داریم:
- API یعنی روشی برای دریافت و ارسال داده (معمولاً JSON) به جای HTML.
- از Flask برای تعریف آدرسها و روشها (
GET,POST, ...) استفاده میکنیم. - با
jsonifyدادهٔ پایتون را به JSON برمیگردانیم. - با
request.get_json()دادهٔ ارسالشده به صورت JSON را میخوانیم. - کدهای وضعیت (
200,201,400,404, ...) به مصرفکنندهٔ API میگویند چه اتفاقی افتاده است. - برای تمرین، ذخیرهسازی در یک لیست در حافظه کاملاً کافی است.
این مفاهیم پایه، قدم اول برای ساخت API های جدیتر در آینده هستند؛ مثلاً اتصال به پایگاه داده، احراز هویت، و استفاده در پروژههای واقعی وب.