responder は Python の Web フレームワークです。 類似のものには Flask や Falcon があります。 公式ページには以下の機能がある、と書かれています。
- A pleasant API, with a single import statement.
- Class-based views without inheritance.
- ASGI framework, the future of Python web services.
- WebSocket support!
- The ability to mount any ASGI / WSGI app at a subroute.
- f-string syntax route declaration.
- Mutable response object, passed into each view. No need to return anything.
- Background tasks, spawned off in a ThreadPoolExecutor.
- GraphQL (with GraphiQL) support!
- OpenAPI schema generation, with interactive documentation!
- Single-page webapp support!
今回は Quick Start! を写経して勉強した際のメモです。
インストールする
事前に responder をインストールしておきます。
pip install responder
サンプル 1 (Hello World!)
基本機能のみ、実装したシンプルなサンプルです。
ソースコード
responder はデフォルトで 127.0.0.1
を Listen するようです。 その為、address='0.0.0.0'
を指定して外部からアクセス出来るようにしています。
import responder api = responder.API() @api.route("/") def hello_world(req, resp): resp.text = "hello, world!" if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
以下のように実行します。
# python app.py INFO: Started server process [18284] INFO: Uvicorn running on http://0.0.0.0:5042 (Press CTRL+C to quit) INFO: Waiting for application startup. INFO: Application startup complete.
結果
$ curl http://127.0.0.1:5042/ hello, world!
サンプル 2 (Accept Route Arguments)
引数を処理するサンプルです。 結果は文字列として返します。
ソースコード
import responder api = responder.API() @api.route("/hello/{who}") def hello_to(req, resp, *, who): resp.text = f"hello, {who}!" if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
結果
curl http://127.0.0.1:5042/hello/alice hello, alice!
サンプル 3 (Returning JSON / YAML)
(文字列では無く) JSON を返すサンプルです。
ソースコード
import responder api = responder.API() @api.route("/hello/{who}/json") def hello_to(req, resp, *, who): resp.media = {"hello": who} if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
Quick Start には下記と書かれていました。
If you want your API to send back JSON, simply set the resp.media property to a JSON-serializable Python object:
結果
curl http://127.0.0.1:5042/hello/alice/json {"hello": "alice"}
サンプル 4 (Rendering a Template)
テンプレートを使ってレンダリングしてみます。
ソースコード
テンプレート自体は templates
ディレクトリに保存しておきます。 今回は templates/hello.html
を以下の内容で新規作成しました。
<html> <head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.3.0/dist/css/uikit.min.css" /> <script src="https://cdn.jsdelivr.net/npm/uikit@3.3.0/dist/js/uikit.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/uikit@3.3.0/dist/js/uikit-icons.min.js"></script> </head> <body> <div class="uk-section uk-section-primary"> <h1 class='uk-heading-primary'>Hello, {{ who }}!</h1> </div> </body> </html>
Python のサンプルコードは以下です。
import responder api = responder.API() @api.route("/hello/{who}/html") def hello_html(req, resp, *, who): resp.html = api.template('hello.html', who=who) if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
結果
サンプル 5 (Setting Response Status Code)
レスポンスコードを指定するサンプルです。
ソースコード
import responder api = responder.API() @api.route("/416") def teapot(req, resp): resp.status_code = api.status_codes.HTTP_416 if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
結果
$ curl -I http://127.0.0.1:5042/416 HTTP/1.1 416 Requested Range Not Satisfiable date: Sat, 25 Jan 2020 03:41:00 GMT server: uvicorn content-type: application/json content-length: 4
サンプル 6 (Setting Response Headers)
レスポンスヘッダを追加するサンプルです。
ソースコード
import responder api = responder.API() @api.route("/pizza") def pizza_pizza(req, resp): resp.headers['X-Pizza'] = '42' if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
結果
curl -I http://127.0.0.1:5042/pizza HTTP/1.1 200 OK date: Sat, 25 Jan 2020 03:45:48 GMT server: uvicorn content-type: application/json x-pizza: 42 content-length: 4
サンプル 7 (Receiving Data & Background Tasks)
ソースコード
import responder api = responder.API() @api.route("/") async def upload_file(req, resp): @api.background.task def process_data(data): f = open('./{}'.format(data['file']['filename']), 'w') f.write(data['file']['content'].decode('utf-8')) f.close() data = await req.media(format='files') process_data(data) resp.media = {'success': 'ok'} if __name__ == '__main__': api.run(address='0.0.0.0', port=5042)
結果 (Python でのテスト)
Responder 公式サイトのテスト用サンプルコードは、なぜか /file
を指定しつつポート番号も 8210
になっています。 これはサーバ側のサンプルコードに合わせて /
ディレクトリに対してアップロードするように修正します。
import requests data = {'file': ('hello.txt', 'hello, world!', "text/plain")} r = requests.post('http://127.0.0.1:5042/', files=data) print(r.text)
実行すると以下のように表示されます。 サーバと同じディレクトリに hello.txt
ファイルがアップロードされているはずです。
$ python uploader.py {"success": "ok"}
結果 (curl でのテスト)
同じ内容を curl で試してみます。 以下の内容で data.txt
というファイルを作成しておきます。
$ cat data.txt hello, world!
これを curl で POST します。
$ curl -F 'file=@data.txt; type=text/plain' http://127.0.0.1:5042/ {"success": "ok"}