Гузариш ба мундариҷа
Мавзӯи 5 аз 6

JSON API

Чаро сайт API мехоҳад

HTML саҳифаро барои инсон тавсиф мекунад, API бошад, ба барномаи дигар дода медиҳад: бот, барномаи мобилӣ ё JavaScript-и браузер. Барнома ба ҷойи таҳлили тегҳо JSON мегирад: формати матнӣ бо объектҳо, массивҳо, сатрҳо, ададҳо, true, false ва null.

Дар Python объекти JSON одатан ба dict, массив ба list, true ба True ва null ба None табдил меёбад. Номҳои калидҳои API қисми шартномаи байни барномаҳо мебошанд: онҳоро танҳо барои зебоии матн дигар кардан мумкин нест. Ҳолат ва навъи ҷавоб низ қисми ҳамин шартномаанд. Ҳар мисоли Python-и зеринро алоҳида иҷро кунед.

1. Луғат ба JSON табдил меёбад

Дар Flask 3.1 луғати аз view баргардонда худкор ба JSON табдил меёбад. Сериализатсия табдили додаҳои Python ба матни интиқолшаванда аст. Flask Content-Type: application/json мегузорад, то мизоҷ форматро фаҳмад.

Методи .get_json()-и ҷавоби санҷишӣ онро боз ба объекти Python табдил медиҳад. Аз ин рӯ санҷидани майдонҳо аз рӯйи калид қулайтар аз муқоисаи сатри хоми JSON аст: фосилаҳо ва тартиби калидҳо метавонанд бе тағйири маъно фарқ кунанд.

from flask import Flask

app = Flask(__name__)

@app.route("/api/status")
def status():
    return {"ready": True, "tasks": 3}

r = app.test_client().get("/api/status")
print(r.status_code, r.mimetype)
data = r.get_json()
print(data["ready"], data["tasks"])

Натиҷа:

200 application/json
True 3

2. Рӯйхатҳо ва рамзи ҳолат

jsonify(...) ҷавоби JSON-ро ошкоро месозад. Он барои рӯйхат ё қимати дигари JSON қулай аст; дар Flask 3.1 рӯйхатро мустақим низ баргардондан мумкин аст. Кортежи (ҷавоб, рамз) ҳолатро новобаста аз муҳтаво тағйир медиҳад. Сохтани муваффақи захира одатан 201 Created, хондани одӣ 200 аст.

Дар натиҷаи Python дидани True ва False маънои JSON-и нодурустро надорад: .get_json() аллакай табдилро иҷро кардааст. Барои дидани формати воқеии интиқол тани ҷавобро бинед.

from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/api/colors")
def colors():
    return jsonify(["red", "blue"])

@app.route("/api/tickets", methods=["POST"])
def ticket():
    return {"id": 7}, 201

client = app.test_client()
print(client.get("/api/colors").get_json())
r = client.post("/api/tickets")
print(r.status_code, r.get_json()["id"])

Натиҷа:

['red', 'blue']
201 7

Рақами доимии билет дар ин ҷо танҳо шакли ҷавобро нишон медиҳад; сабтҳои воқеӣ нигоҳдорӣ ва рақамҳои ягона мехоҳанд.

3. JSON дар тани дархост

request.get_json() тани JSON-ро бо Content-Type-и мувофиқ мехонад ва таҳлил мекунад. JSON-и вайрон одатан 400 ва навъи номувофиқи муҳтаво 415 медиҳад. silent=True ба ҷойи ин хатоҳо None бармегардонад, то ҷавоби худро интихоб кунед.

JSON-и дуруст ҳатман объект нест: [], 42 ва null низ дурустанд. Аввал навъи умумӣ, баъд мавҷудият ва навъи майдонҳо, сипас маҳдудиятҳо санҷида мешаванд. Дар мисол type(value) is int қасдан True-ро рад мекунад: дар Python bool зерсинфи int аст.

from flask import Flask, request

app = Flask(__name__)

@app.route("/api/check", methods=["POST"])
def check_value():
    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return {"valid": False}, 400
    value = data.get("value")
    if type(value) is not int or not 1 <= value <= 5:
        return {"valid": False}, 400
    return {"valid": True}

client = app.test_client()
for payload in ({"value": 3}, [], {"value": True}):
    r = client.post("/api/check", json=payload)
    print(r.status_code, r.get_json()["valid"])
r = client.post("/api/check", data="broken", content_type="application/json")
print(r.status_code, r.get_json()["valid"])

Натиҷа:

200 True
400 False
400 False
400 False

Аргументи json=... дар мизоҷи санҷишӣ ҳам қиматро рамзгузорӣ мекунад ва ҳам Content-Type мегузорад. data=... тани дигар мефиристад: луғат одатан ба додаҳои форма табдил меёбад, сатр бошад, бетағйир меравад. Пас request.form ҷойгузини request.get_json() нест.

4. Захира, усул ва ҷавоби холӣ

Дар равиши REST нишонӣ захира ва усули HTTP амалро муайян мекунад. GET мехонад, POST месозад, PUT намоиши захираро иваз мекунад ва DELETE ҳазф мекунад. Захираи набуда одатан 404 медиҳад; Flask усули дар methods набударо бо 405 рад мекунад.

Барои ҳазфи муваффақ аксар вақт 204 No Content истифода мешавад. Ин ҷавоб тан надорад: ҳатто агар бо 204 матн баргардонед, Flask пеш аз фиристодан танро мебардорад. Дар поён кеши хурди муваққатӣ аст: PUT сохтори пешакӣ маълумро қабул мекунад; барои додаҳои дилхоҳ санҷишро мисли бахши пешин илова кардан лозим аст.

from flask import Flask, request

app = Flask(__name__)
CACHE = {"value": 2}

@app.route("/api/cache", methods=["GET", "PUT", "DELETE"])
def cache():
    if request.method == "DELETE":
        CACHE.clear()
        return "", 204
    if request.method == "PUT":
        CACHE.clear()
        CACHE.update(request.get_json())
    return dict(CACHE)

client = app.test_client()
print(client.get("/api/cache").get_json())
print(client.put("/api/cache", json={"value": 9}).get_json())
r = client.delete("/api/cache")
print(r.status_code, len(r.data))
print(client.get("/api/cache").get_json())
print(client.post("/api/cache").status_code)

Натиҷа:

{'value': 2}
{'value': 9}
204 0
{}
405

Дар дохил чӣ тавр кор мекунад

Аввал масирёб роҳ ва усулро месанҷад. Баъд view аз тан объекти Python гирифта, шартномаи додаҳоро месанҷад ва амалро иҷро мекунад. JSON-провайдери Flask қиматҳои баргардондаро сериализатсия карда, сарлавҳа мегузорад. Мизоҷ ҳолат ва форматро санҷида, танро мекушояд. Саҳифаи HTML-и хато танҳо аз сабаби бо /api/ оғоз шудани нишонӣ JSON намешавад: формати хаторо алоҳида муайян кунед.

Хатоҳои маъмул ва ислоҳи онҳо

  1. return str({"ok": True}) матни Python бо True ва нохунакҳои якка медиҳад, на JSON. Ислоҳ: луғат ё jsonify баргардонед.
  2. data["value"] фавран пас аз таҳлил барои рӯйхат, None ё объекти бе майдон хато медиҳад. Ислоҳ: аввал навъи тан, баъд майдонро хонда санҷед.
  3. isinstance(True, int) қимати True медиҳад ва булевӣ аз санҷиши ададӣ мегузарад. Ислоҳ: агар шартнома маҳз адади бутун хоҳад, type(value) is int истифода кунед.
  4. return {"deleted": True}, 204 ин танро ба мизоҷ намерасонад. Ислоҳ: сатри холӣ бо 204 диҳед ё агар тан лозим бошад, 200 интихоб кунед.

Ёддошт

Амал Навишт
Ҷавоби JSON return {"ok": True} ё jsonify(...)
Сохтан return data, 201
Таҳлил бе хатои худкор request.get_json(silent=True)
Санҷиши объект isinstance(data, dict)
Фиристодан дар санҷиш client.post(path, json=payload)
Иваз / ҳазф client.put(...) / client.delete(...)
Хондани ҷавоб r.get_json()
Ҷавоби бе тан return "", 204

Дар API ҳам дархости муваффақ, ҳам навъи нодурусти тан ва ҳам майдонҳои набударо санҷед. Барномаҳо ҳолати пешгӯишаванда ва сохтори яксони ҷавоб мехоҳанд.