たけし備忘録

自分の好奇心の赴くままに勉強メモ LL系が大好き Python bash Julia C

3.ルーティングリクエスト

ルーティング要求:Request Routing

さきほどの2.クイックスタートでは、単一のルートで非常に単純なWebアプリケーションを構築しました。ここでもう一度"Hello World"の例のルーティング部分を見てみましょう。次のとおりです。

@route('/hello')
def hello():
    return "Hello World!"

 route() デコレータは、コールバック関数へのURLパスをつなぐと、デフォルトのアプリケーションに新たなルートを追加します。一つの経路のみのアプリケーションは、ボーリングのようなものですけどね。ですのでさらにいくつか追加してみましょう。(ただし from bottle import template とするのを忘れずに)

from bottle import template
#中略
@route('/')
@route('/hello/<name>')
def greet(name='Stranger'):
    return template('Hello {{name}}, how are you?', name=name)

 この例では、2つのことを示しています。

  • 単一のコールバック関数に複数のルートにバインド(リンク)することができること。
  • URLにワイルドカード(この場合は)を追加し、キーワード引数を介してアクセスすることができる。

 ということです。


ダイナミックルート

 単純なワイルドカードは、角括弧で囲まれた名前(例えば <name> 、<filename>)から成り、スラッシュに1つまたは複数の文字を受け入れます。
 ワイルドカードを含むルートがダイナミックルートで呼ばれる(静的ルートとは対照的に)と、同時に複数のURLと一致します。
 たとえば、/hello/ というルートは、/hello/alice 並びに /hello/bob というリクエストを受け取ります。これは /hello/hello/mr/smith のようには受け取りません。
 各ワイルドカードはリクエストのコールバック関数へキーワード引数としてURLの<>の中身を渡します。
 これらを使用すると早く簡単にREST風に、見栄え良く、有意義なURLを実装することができます。ここにURLと一緒にいくつかの例を示しました。次のとおりです。

@route('/wiki/<pagename>') # /wiki/Learning_Python などと一致
def show_wiki_page(pagename):
...
@route('/<action>/<user>') # /follow/defnull などと一致
def user_api(action, user):
...


  • フィルター

 フィルタは、コールバック関数に渡される前にURLのワイルドカード部分(<name>の部分)をより具体的なワイルドカードで定義する、または変換するために使用されます。
 フィルタのワイルドカード<name:filter> または <name:filter:config> などと宣言されます。
 オプションの設定部分の構文は、使用されているフィルタに依存しています。
 このようなフィルタを用いると、シェルスクリプトで言うフィルタのようなことが出来ます。
 <name:filter> で定義したルートの場合、nameというワイルドカードにfilterという条件(整数、小数、パス、正規表現など)を設け、その条件に一致するもののみがワイルドカードとしてコールバック関数に渡されます。

 次のフィルタはデフォルトで実装されているものです。今後さらに追加されることもあります。

  • :int int型のみの一致(符号付き)で、数字と値を整数に変換します。
  • :float :int に似ていますが浮動小数のためのものです。
  • :path : pathは、非欲張りの下でスラッシュ文字を含むすべての文字と一致し、複数のパスセグメントを一致させるために使用することができます。
  • :re : reは、configフィールドにカスタムの正規表現を指定することができます。一致した値は変更されません。


それでは、いくつかの実用的な例を見てみましょう:

@route('/object/<id:int>')       # /object/123などのようなint型のみ通す /object/abcなどはエラー
def callback(id):
    assert isinstance(id, int)

@route('/show/<name:re:[a-z]+>') # /show/abc(1文字以上の小文字アルファベット)は通す
def callback(name):
    assert name.isalpha()

@route('/static/<path:path>')    # /static/dir/file/hello.pyの場合は関数にdir/file/hello.pyを渡す
def callback(path):
    return static_file(path, ...)


 さらに独自のフィルタを追加することもできます。詳細についてはRequest Routing — Bottle 0.13-dev documentationを参照してください。


HTTPリクエストメソッド

 HTTPプロトコルは、さまざまなタスクのいくつかのリクエストのメソッド(または"動詞"とも呼ばれる。GETなど)を定義しています。
 メソッドの指定をしていない場合はすべてのルートのデフォルトはGETになります。これらのルートは、GETリクエストにのみマッチします。
 
 POST、PUT、DELETEやPATCHなど、他のメソッドを使う場合には route()デコレータにそのmethodキーワード引数を追加するか、それぞれのメソッドのデコレータ get(), post(), delete(), patch()を使います。
 POSTメソッドは、一般にはHTMLフォームの送信に使用されます。この例では、POSTを使用してログインフォームを処理する方法を示しています。

from bottle import get, post, request #または route

@get('/login') # または @route('/login')
def login:
    return '''
        <form action="/login" method="post">
            Username: <input name="usename" type="text" />
            Password: <input name="password" type="password" />
            <input value="Login" type="submit" />
        </form>
    '''
    
@post('/login') # または @route('/login', method='POST')
def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):
        return "<p>Your login information was correct.</p>"
    else:
        return "<p>Login failed.</p>"

 
 この例では、/login URLは、二つのコールバック、GETリクエストおよびPOSTリクエストのために別の関数にリンクされています。
 1つ目のloginコールバックは、ユーザーにHTMLフォームが表示されます。
 2つ目のdo_loginコールバックは、フォームの送信(サブミット)時に呼び出され、ユーザーがフォームに入力したログイン情報がチェックされます。 Request.formsの使い方は5.データリクエスト - たけし備忘録に記載されています。


特殊なメソッド:HEADとANY

 HEADメソッドはGETリクエストと同じようなリクエストをしますが、レスポンスはボディを返しません。
 これは、文書全体をダウンロードせずに、文書に関するメタ情報(すなわちヘッダ情報)のみを取得するのに便利です。
 HEADが指定される場合、Bottleは対応するGETルートに沿ってフォールバック(後退)し、GETリクエストのボディを切断することによって、自動的にこれらのリクエストを処理します。HEADルートを自分で指定する必要はありません。
 さらに、非標準のメソッドはすべて低い優先度のフォールバックとして動作します。
 ANYに耳を傾けるのルートに関係なく、HTTPメソッドの要求と一致しますが、他のより具体的なルートが定義されていない場合のみです。これは、サブアプリケーションより具体的に要求をリダイレクトするプロキシのルートのために有用です。
 それを要約すると:HEADリクエストは、経路をGETにフォールバックし、すべての要求は、任意のルートにフォールバックしますが、元のリクエストメソッドに対して一致するルートが存在しない場合のみです。それはそれと同じくらい簡単です。


静的ファイルのルーティング

 画像やCSSファイルなどの静的ファイルは自動的に提供されません。
 ルートに提供されるファイルと、ファイルを見つけるための場所を制御するコールバックを追加する必要があります。

from bottle import static_file
@route('/static/<filename>')
def server_static(filename):
    return static_file(filename, root='/path/to/your/static/files')

 static_file() 関数は、(4.コンテンツの生成/静的ファイルを参照してください)​​安全かつ便利にファイルを提供するためのヘルパーです。
 <filename>というワイルドカードはスラッシュを挟むとパスと一致しません。この例では /path/to/your/static/files に制限されていますので、サブディレクトリ内のファイルを提供するには、path フィルタ(:path)を使用します。ワイルドカード<filename>ではなくpathフィルター(:path)を追加して<filepath:path>を使用します。

@route('/static/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='/path/to/your/static/files')

 rootなどの相対的なルートパス= ./static/filesなどを指定するときは注意してください。
 作業ディレクトリ(./)とプロジェクトディレクトリは、常に同じではありません。


エラーページ

 何かがうまくいかない場合、Bottleは有益でシンプルなエラーページが表示されます。
 error() デコレータの持つ特定のHTTPステータスコード(404など)を上書きすることができます。

from bottle import error
@error(404)
def error404(error):
    return 'Nothing here, sorry'

 これを記述することで、"404 File not Found errors" というエラーがユーザーにカスタムエラーページに表示されます。
 エラーハンドラに渡される唯一のパラメータはHTTPErrorのインスタンスです。
 それとは別に、エラーハンドラは通常のリクエストのコールバックと非常によく似ています。
 リクエストから読み取られた応答への書き込みとHTTPErrorインスタンスを除くサポートされている全てのデータ型を返すことができます。
 アプリケーションが返すかHTTPErrorが発生した場合にのみ、エラーハンドラが使用されます。(abort() だけではありません)
 Request.statusを変更したり、HttpResponseが生じるなどではエラーハンドラは起動しません。


次の記事へ進む → 4.コンテンツの生成


関連リンク