Python responder の Quick Start を写経してみる
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 をインストールしておきます。
基本機能のみ、実装したシンプルなサンプルです。
ソースコード
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!
引数を処理するサンプルです。 結果は文字列として返します。
ソースコード
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!
(文字列では無く) 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" }
テンプレートを使ってレンダリングしてみます。
ソースコード
テンプレート自体は templates
ディレクトリに保存しておきます。 今回は templates/hello.html
を以下の内容で新規作成しました。
1
2
3
4
5
6
7
8
9
10
11
12 <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 )
結果
レスポンスコードを指定するサンプルです。
ソースコード
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
レスポンスヘッダを追加するサンプルです。
ソースコード
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
ソースコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 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" }