生成した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するみたいなコードになっている。