Python講座 / FlaskでJSON APIを作り入力を検証する
FlaskのAPIはHTTPリクエストをPython関数へ対応させます。JSONオブジェクト、必須項目、型と範囲を検査し、作成成功と不正入力をステータスコードで区別します。
ローカル開発の範囲を決める
このAPIは同じPC内の実習です。メモリー上のリストはプロセス終了で消え、独立したサーバープロセス間でも共有されません。公開サービスには永続化、認証、認可、配備と同時アクセスの設計が必要です。
プロジェクトの仮想環境へ導入します。
python -m pip install flask
app.pyとして保存してpython app.pyで起動します。5000番が使用中なら、ポートとクライアントのアドレスを一緒に変更します。
JSON表現と永続保存の違いは、JSONデータを変換して保存するで扱う規則とつながります。
APIアプリケーション全体を保存する
次をapp.pyへ保存します。
from flask import Flask, jsonify, request
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 4096
expenses = []
@app.errorhandler(413)
def too_large(error):
return jsonify(error="request body too large"), 413
@app.get("/api/expenses")
def list_expenses():
return jsonify(expenses)
@app.post("/api/expenses")
def add_expense():
data = request.get_json(silent=True)
if not isinstance(data, dict):
return jsonify(error="expected JSON object"), 400
name, amount = data.get("name"), data.get("amount")
if not isinstance(name, str) or not name.strip() or type(amount) is not int or amount < 0:
return jsonify(error="invalid name or amount"), 400
row = {"id": len(expenses) + 1, "name": name.strip(), "amount": amount}
expenses.append(row)
return jsonify(row), 201
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=False)
GET /api/expensesは一覧、POSTは一件作成です。成功は201、検証エラーは400を返します。MAX_CONTENT_LENGTHはリクエスト本文のサイズを制限しますが、アプリに必要なすべての資源制限を代行する設定ではありません。
テストクライアントで経路を確認する
同じフォルダーへtest_client.pyを保存します。
from app import app, expenses
expenses.clear()
with app.test_client() as client:
created = client.post("/api/expenses", json={"name":"lunch", "amount":8000})
assert created.status_code == 201
for payload in [{"name":"x","amount":True}, {"name":"x","amount":-1}, {"name":" ","amount":1}, [1,2]]:
response = client.post("/api/expenses", json=payload)
assert response.status_code == 400 and response.is_json
assert client.post("/api/expenses", data="{", content_type="application/json").status_code == 400
too_large = client.post("/api/expenses", data="x"*5000, content_type="application/json")
assert too_large.status_code == 413 and too_large.is_json
rows = client.get("/api/expenses")
assert rows.status_code == 200 and len(rows.get_json()) == 1
print(created.status_code, rows.get_json()[0]["amount"])
print("validation and 413 checks passed")
実行します。
python test_client.py
予想結果は201 8000です。テストクライアントは別に起動したサーバーなしでFlaskの処理経路を呼びます。実際のネットワーク、リバースプロキシ、配備環境まで検証するものではありません。
型とアプリの寿命を確認する
JSONのtrueはboolになり、isinstance(True, int)はTrueです。そのため金額ではtypeによる厳密な整数検査で除外します。silent=Trueでは不正なJSONがNoneとなり、定義した入力エラーへ進みます。
開発サーバーはローカルに保ち、対話型デバッガーを公開しないでください。Ctrl+Cで止めて再起動すると一覧は消えます。永続化にはSQLiteなどの保存規則が必要です。リストの長さから作るIDも、この管理された実習用であり、同時利用する本番の識別と同期の代わりにはなりません。
関連する公式リファレンス
MAX_CONTENT_LENGTH超過は413で、Flaskの既定エラー画面はHTMLの場合があります。上の専用ハンドラーでJSONにそろえました。不正JSON・配列・bool金額・負数・空白名は400で保存しません。拡張テストの最後は validation and 413 checks passed です。この契約はアプリで扱う経路とエラーの範囲であり、前段のWebサーバーやプロキシが拒否した要求までは保証しません。
動作と適用条件の公式説明:Flask: handling application errors · Flask: MAX_CONTENT_LENGTH




