概要
content-type: application/xml
or content-type: application/json
どちらでも受けれるような API を考えます
環境
- macOS 13.4
- Python 3.11.3
- Flask 2.3.2
方針
- デコレータを使って xml -> dict をいい感じに変換する
- 最終的には変換はオブジェクトに変換する
- 各種エラー対応はできる限りしっかりしたい
- レスポンスに対しては現在は考慮しない
サンプルコード
-
vim app.py
import copy
import json
import traceback
from json.decoder import JSONDecodeError
from xml.parsers.expat import ExpatError
import xmltodict
from flask import Flask, request
app = Flask(__name__)
class User:
def __init__(self, name: str = "default_user", **_) -> None:
self.name = name
self.age = 10
# dict から User オブジェクトへの変換
def convert_obj(func):
def wrapper(*args, content_dict: dict, **kwargs):
try:
user = User(**content_dict["xml"])
except KeyError:
# データ構造が正しくない場合はエラー
raise ValueError("Root attribute must start with 'xml'.")
return func(*args, user=user, **kwargs)
return wrapper
# xml から dict への変換、すでに dict の場合は何もしない
def convert_dict(func):
def wrapper(*args, **kwargs):
data = copy.copy(request.data)
try:
data = xmltodict.parse(data)
except ExpatError:
try:
data = json.loads(data)
except JSONDecodeError:
# xml じゃないかつ dict じゃない場合はエラー
raise ValueError("You xml or dict must be specified.")
return func(*args, content_dict=data, **kwargs)
return wrapper
@app.route("/", methods=["GET", "POST"])
@convert_dict
@convert_obj
def parse_xml(user: User):
return user.name
@app.errorhandler(ValueError)
def handle_bad_request(e):
return traceback.format_exception_only(e), 400
動作確認
正常系
curl localhost:5000 -XPOST -d '{"xml": {"name":"hawksnowlog"}}' -H 'content-type: application/json'
curl localhost:5000 -XPOST -d '<xml><name>hawksnowlog</name></xml>' -H 'content-type: application/xml' -H 'Accept: application/xml'
異常系
curl localhost:5000 -XPOST -d '{"xml2": {"name":"hawksnowlog"}}' -H 'content-type: application/json'
curl localhost:5000 -XPOST -d 'hoge' -H 'content-type: application/json'
最後に
レスポンスは accept ヘッダのタイプに応じて勝手に変換してくれるようになっているとかっこいい
0 件のコメント:
コメントを投稿