生成したmarshmallowのschemaをwrapしてpyramidから使えるようにしてみた
swaggerからmarshmallowのschemaを生成する機能は昔から作っていて、デフォルトでは definitions 部分だけしか見ないのだけれど。--full というオプションをつけると paths 以下の parameters や responses も見るようになっている。ここで生成したschemaをpyramidから使えるようにした。まだpypiにはあげていない。
だいたい以下の様な手順で使う。
$ pip install swagger-marshmallow-codegen $ swagger-marshmallow-codegen swagger.yml --full > schema.py
あとは以下の様な感じでコードを書けばOK。以下が重要。
config.include("toybox.swagger")- withswaggerで生成されたschemaを渡したdecoratorをつける
from datetime import datetime, timedelta from toybox.simpleapi import simple_view, run from toybox.swagger import withswagger import schema # ./schema.py @simple_view("/", decorator=withswagger(input=schema.Input, output=schema.Output)) def hello(request): return {'message': 'Welcome, {}!'.format(request.GET["name"])} @simple_view("/add", request_method="POST", decorator=withswagger(input=schema.AddInput, output=schema.AddOutput)) def add(request): x = request.json["x"] y = request.json["y"] return {"result": x + y} @simple_view("/dateadd", request_method="POST", decorator=withswagger(input=schema.DateaddInput, output=schema.DateaddOutput)) def dateadd(request): value = request.json["value"] addend = request.json["addend"] unit = request.json["unit"] value = value or datetime.utcnow() if unit == 'minutes': delta = timedelta(minutes=addend) else: delta = timedelta(days=addend) result = value + delta return {'result': result} if __name__ == "__main__": import logging logging.basicConfig(level=logging.DEBUG) run.include("toybox.swagger") run(port=5001)
viewの内部では全てswaggerのspecを満たしたrequsestであることが保証されている。ダメだった場合にはHTTPBadRequestが返る。
例えば以下の様な感じ
$ http POST :5001/add x=10 y=20
HTTP/1.0 200 OK
Content-Length: 14
Content-Type: application/json
Date: Sat, 18 Mar 2017 18:24:52 GMT
Server: WSGIServer/0.2 CPython/3.5.2
{
"result": 30
}
$ http POST :5001/add x=10
HTTP/1.0 400 Bad Request
Content-Length: 107
Content-Type: application/json
Date: Sat, 18 Mar 2017 18:24:55 GMT
Server: WSGIServer/0.2 CPython/3.5.2
{
"code": "400 Bad Request",
"message": {
"y": [
"Missing data for required field."
]
},
"title": "Bad Request"
}
$ http POST :5001/add
HTTP/1.0 400 Bad Request
Content-Length: 214
Content-Type: application/json
Date: Sat, 18 Mar 2017 18:24:57 GMT
Server: WSGIServer/0.2 CPython/3.5.2
{
"code": "400 Bad Request",
"message": "The server could not comply with the request since it is either malformed or otherwise incorrect.\n\n\nExpecting value: line 1 column 1 (char 0)\n\n",
"title": "Bad Request"
}
viewへのinputとして渡せるのはswaggerと同様に以下。
- path –
/foo/{name}みたいなやつ - query –
/?foo=barみたいなやつ - header – request header
- body – REST APIなどでjsonをpostしたときのやつ
- form – 普通のPOST
example全体は以下のリンク先。
https://github.com/podhmo/toybox/tree/master/examples/swagger
どうでも良いこと
ところでmarshmallowのvalidationがload(deserialization)時にだけ掛かるということに気づき衝撃を受けた。しょうがないのでserialize後deserializeするみたいなコードになっている。