# ルーティング
これまでに様々な形の装飾家を見てきました。
でも何なの?どうやって使うのか?
@app.route("/stairway")
...
@app.get("/to")
...
@app.post("/heaven")
...
# ルートを追加する
ハンドラをエンドポイントに接続する最も基本的な方法は、app.add_route()
を使用することです。
詳細については、API docs (opens new window)を参照してください。
async def handler(request):
return text("OK")
app.add_route(handler, "/test")
デフォルトでは、ルートはHTTPGET
コールとして使用できます。1つ以上のHTTPメソッドに応答するようにハンドラを変更できます。
app.add_route(
handler,
'/test',
methods=["POST", "PUT"],
)
デコレータ構文を使用する場合、前の例はこれと同じです。
@app.route('/test', methods=["POST", "PUT"])
async def handler(request):
return text('OK')
# HTTPメソッド
標準のHTTPメソッドにはそれぞれ便利なデコレータがあります。
WARNING
デフォルトでは、Sanic は安全でない HTTP メソッド (POST
、PUT
、PATCH
) で受信したリクエストボディのみを消費します。他のメソッドでHTTPリクエストのデータを受け取りたい場合は、以下の2つのオプションのいずれかを実行する必要があります。
オプション#1 - ignore_body
を使用してSanicにボディを消費するように指示する。
@app.delete("/path", ignore_body=False)
async def handler(_):
...
オプション #2 - ハンドラ内で receive_body
を使って手動でボディを消費する。
@app.delete("/path")
async def handler(request: Request):
await request.receive_body()
# パスパラメーター
Sanicでは、パターンマッチングやURLパスからの値の抽出が可能です。これらのパラメータは、キーワード引数としてルートハンドラに挿入されます。
@app.get("/tag/<tag>")
async def tag_handler(request, tag):
return text("Tag - {}".format(tag))
パラメータの型を宣言できます。これはマッチング時に強制され、変数を型キャストします。
@app.get("/foo/<foo_id:uuid>")
async def uuid_handler(request, foo_id: UUID):
return text("UUID - {}".format(foo_id))
# 型のサポート
# 正規表現照合
複雑なルーティングと比較すると、上記の例は単純すぎることが多く、まったく異なるルーティングマッチングパターンを使用するため、ここではregexマッチングの高度な使用方法について詳しく説明します。
ルートの一部を照合する場合があります。
/image/123456789.jpg
ファイルのパターンにマッチさせたいが、数値の部分だけをキャプチャしたい場合は、正規表現を使う必要があります😄:
app.route(r"/image/<img_id:(?P<img_id>\d+)\.jpg>")
さらに、これらはすべて許容可能です。
@app.get(r"/<foo:[a-z]{3}.txt>") # matching on the full pattern
@app.get(r"/<foo:([a-z]{3}).txt>") # defining a single matching group
@app.get(r"/<foo:(?P<foo>[a-z]{3}).txt>") # defining a single named matching group
@app.get(r"/<foo:(?P<foo>[a-z]{3}).(?:txt)>") # defining a single named matching group, with one or more non-matching groups
また、名前付き一致グループを使用する場合は、セグメントラベルと同じである必要があります。
@app.get(r"/<foo:(?P<foo>\d+).jpg>") # OK
@app.get(r"/<foo:(?P<bar>\d+).jpg>") # NOT OK
より一般的な使用方法については、正規表現の操作 (opens new window)を参照してください。
# URLを生成
Sanicは、ハンドラメソッド名app.url_for()
に基づいてURLを生成するメソッドを提供しています。これは、アプリケーションへのURLパスをハードコーディングしない場合に便利です。代わりに、ハンドラ名を参照することができます。
@app.route('/')
async def index(request):
# generate a URL for the endpoint `post_handler`
url = app.url_for('post_handler', post_id=5)
# Redirect to `/posts/5`
return redirect(url)
@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
...
任意の数のキーワード引数を渡すことができます。_not_a要求パラメータであるものはすべて、クエリ文字列の一部として実装されます。
>>> app.url_for(
"post_handler",
post_id=5,
arg_one="one",
arg_two="two",
)
'/posts/5?arg_one=one&arg_two=two'
また、1つのクエリキーに複数の値を渡すこともサポートされています。
>>> app.url_for(
"post_handler",
post_id=5,
arg_one=["one", "two"],
)
'/posts/5?arg_one=one&arg_one=two'
# 特殊のキーワード
See API Docs for more details.
>>> app.url_for("post_handler", post_id=5, arg_one="one", _anchor="anchor")
'/posts/5?arg_one=one#anchor'
# _external requires you to pass an argument _server or set SERVER_NAME in app.config if not url will be same as no _external
>>> app.url_for("post_handler", post_id=5, arg_one="one", _external=True)
'//server/posts/5?arg_one=one'
# when specifying _scheme, _external must be True
>>> app.url_for("post_handler", post_id=5, arg_one="one", _scheme="http", _external=True)
'http://server/posts/5?arg_one=one'
# you can pass all special arguments at once
>>> app.url_for("post_handler", post_id=5, arg_one=["one", "two"], arg_two=2, _anchor="anchor", _scheme="http", _external=True, _server="another_server:8888")
'http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor'
# ルート名をカスタマイズ
カスタムルート名は、ルートの登録時にname
引数を渡すことで使用できます。
@app.get("/get", name="get_handler")
def handler(request):
return text("OK")
ここで、このカスタム名を使用してURLを取得します。
>>> app.url_for("get_handler", foo="bar")
'/get?foo=bar'
# Websockets routes
WebsocketルーティングはHTTPメソッドと同様に動作します。
async def handler(request, ws):
messgage = "Start"
while True:
await ws.send(message)
message = ws.recv()
app.add_websocket_route(handler, "/test")
便利なデコレーターも付いています。
@app.websocket("/test")
async def handler(request, ws):
messgage = "Start"
while True:
await ws.send(message)
message = ws.recv()
websockets sectionを読み、動作の仕組みを学んでください。
# 厳密なスラッシュ
Sanicルートは、末尾のスラッシュがあるかどうかに厳密に一致するように設定できます。これはいくつかのレベルで設定でき、次の優先順位に従います。
- Route
- Blueprint
- BlueprintGroup
- Application
# provide default strict_slashes value for all routes
app = Sanic(__file__, strict_slashes=True)
# overwrite strict_slashes value for specific route
@app.get("/get", strict_slashes=False)
def handler(request):
return text("OK")
# it also works for blueprints
bp = Blueprint(__file__, strict_slashes=True)
@bp.get("/bp/get", strict_slashes=False)
def handler(request):
return text("OK")
bp1 = Blueprint(name="bp1", url_prefix="/bp1")
bp2 = Blueprint(
name="bp2",
url_prefix="/bp2",
strict_slashes=False,
)
# This will enforce strict slashes check on the routes
# under bp1 but ignore bp2 as that has an explicitly
# set the strict slashes check to false
group = Blueprint.group([bp1, bp2], strict_slashes=True)
# Staticファイル
Sanicから静的ファイルを提供するには、app.static()
を使用します。
引数の順序は重要です。
- ファイルが提供されるルート
- サーバー上のファイルへのパス
詳しくはAPI docsを見てください。
app.static("/static", "/path/to/directory")
個々のファイルを提供することもできます。
app.static("/", "/path/to/index.html")
また、エンドポイントに名前を付けると便利な場合もあります。
app.static(
"/user/uploads",
"/path/to/uploads",
name="uploads",
)
URLの取得は、ハンドラと同様に機能します。ただし、ディレクトリ内に特定のファイルが必要な場合は、filename
引数を追加することもできます。
>>> app.url_for(
"static",
name="static",
filename="file.txt",
)
'/static/file.txt'
```python
>>> app.url_for(
"static",
name="uploads",
filename="image.png",
)
'/user/uploads/image.png'
TIP
複数のstatic()
ルートを使用する場合は、手動で名前を付けることを推奨します。これにより、バグを発見するのが難しい可能性がほぼ確実に軽減されます。
app.static("/user/uploads", "/path/to/uploads", name="uploads")
app.static("/user/profile", "/path/to/profile", name="profile_pics")
# ルートコンテキスト
NEW in v21.12
ルートが定義されるとき、ctx_
という接頭辞を持つキーワード引数をいくつでも追加することができます。これらの値はルートの ctx
オブジェクトにインジェクションされます。
@app.get("/1", ctx_label="something")
async def handler1(request):
...
@app.get("/2", ctx_label="something")
async def handler2(request):
...
@app.get("/99")
async def handler99(request):
...
@app.on_request
async def do_something(request):
if request.route.ctx.label == "something":
...