7.プラグイン
プラグイン
バージョン0.9の新機能。
ボトルのコア機能は、ケースを使用する最も一般的なカバーですが、マイクロフレームワークとしてそれには限界がある。 "プラグイン"が遊びに来る場所です。プラグインは、フレームワークに欠けている機能を追加するサードパーティ製のライブラリを統合する、または単にいくつかの反復作業を自動化します。
我々は、使用可能なプラグインの成長のリストを持っており、ほとんどのプラグインは、アプリケーション間での移植性、再利用できるように設計されています。チャンスはあなたの問題がすでに解決されたことを高く、すぐに使用できるプラグインが存在しています。されていない場合は、プラグイン開発ガイドはあなたを助けるかもしれません。
エフェクトとプラグインのAPIは、多様であり、特定のプラグインに依存しています。例えばSQLitePluginプラグインは、dbのキーワード引数を必要とするコールバックを検出し、新しいデータベース接続オブジェクトのコールバックが呼び出されるたびに作成されます。これは非常に便利なデータベースを使用できるようになります。
from bottle import route, install, template from bottle_sqlite import SQLitePlugin install(SQLitePlugin(dbfile='/tmp/test.db')) @route('/show/<post_id:int>') def show(db, post_id): c = db.execute('SELECT title, content FROM posts WHERE id = ?', (post_id,)) row = c.fetchone() return template('show_post', title=row['title'], text=row['content']) @route('/contact') def contact_page(): ''' This callback does not need a db connection. Because the 'db' keyword argument is missing, the sqlite plugin ignores this callback completely. ''' return template('contact')
他のプラグインは、スレッドセーフなローカル·オブジェクトを移入し、要求オブジェクトの詳細を変更するには、コールバックによって返されるデータをフィルタ処理したり、完全にコールバックを回避する可能性があります。例えば、 "auth"というプラグインが有効なセッションをチェックし、元のコールバックを呼び出す代わりに、ログインページを返すことができます。正確には何が起こるかはプラグインに依存しています。
アプリケーション全体のインストール
プラグインは、アプリケーション全体または単に追加の機能を必要とするいくつかの特定のルートにインストールすることができます。ほとんどのプラグインは、安全にすべてのルートにインストールされ、その機能を必要としないコールバックにオーバーヘッドを追加しないように十分にスマートであることができます。
私たちは例えばSQLitePluginプラグインを見てみましょう。それだけでデータベース接続を必要とするルートのコールに影響を与えます。他のルートはそのままにしています。このため、我々は追加のオーバーヘッドがアプリケーション全体のプラグインをインストールすることができます。
プラグインをインストールするには、(インストール)を呼び出す最初の引数としてプラグインを使って:
from bottle_sqlite import SQLitePlugin install(SQLitePlugin(dbfile='/tmp/test.db'))
プラグインはまだルートのコールには適用されません。これがないルートが失われていないことを確認するために延期されます。は、まずプラグインをインストールし、変更したい場合は、後でルートを追加することができます。インストールされているプラグインの順序は、しかし、重要である。プラグインがデータベース接続を必要とする場合には、まずデータベースプラグインをインストールする必要があります。
プラグインのアンインストール
あなたは()以前にインストールしたプラグインをアンインストールするには、名前、クラス、またはインスタンスを使用することができます。
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db') install(sqlite_plugin) uninstall(sqlite_plugin) # uninstall a specific plugin uninstall(SQLitePlugin) # uninstall all plugins of that type uninstall('sqlite') # uninstall all plugins with that name uninstall(True) # uninstall all plugins at once
リクエストを処理しながら、プラグインがあっても、実行時に、いつでもインストールおよび削除することができます。これは、いくつかのきちんとしたトリックを(スロー·デバッグをインストールしたり、必要なプラグインだけをプロファイリング)を有効にしますが、使い古されてはいけません。たびにプラグインの変更点のリストは、ルートキャッシュがフラッシュされ、すべてのプラグインが再適用されます。
注意
モジュールレベルのインストール()とアンインストール()関数は、デフォルトのアプリケーションに影響を与えます。特定のアプリケーション用のプラグインを管理するには、ボトルのアプリケーションオブジェクト上の対応するメソッドを使用しています。
ルート固有のインストール
ルートの適用パラメータ()を使用すると、経路のごく少数にプラグインをインストールする場合は、デコレータに便利です。
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db') @route('/create', apply=[sqlite_plugin]) def create(db): db.execute('INSERT INTO ...')
明示的にルートの数のプラグインを無効にすることができます。ルート()デコレータは、この目的のためのスキップ·パラメータがあります。
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db') install(sqlite_plugin) @route('/open/<db>', skip=[sqlite_plugin]) def open_db(db): # The 'db' keyword argument is not touched by the plugin this time. # The plugin handle can be used for runtime configuration, too. if db in ('test', 'test2'): sqlite_plugin.dbfile = '/tmp/%s.db' % db return "Database File switched to: /tmp/%s.db" % db abort(404, "No such database.")
スキップ·パラメータは、単一の値または値のリストを受け入れます。あなたはスキップされるプラグインを識別するための名前、クラス、またはインスタンスを使用することができます。一度にすべてのプラグインをスキップする= Trueをスキップして設定します。
プラグインとサブアプリケーション
ほとんどのプラグインは、それらがインストールされたアプリケーションに固有のものです。その結果、彼らはBottle.mount()でマウントされたサブアプリケーションに影響を及ぼすべきではありません。次に例を示します。
root = Bottle() root.mount('/blog', apps.blog) @root.route('/contact', template='contact') def contact(): return {'email': 'contact@example.com'} root.install(plugins.WTForms())
あなたがアプリケーションをマウントするたびに、ボトルには、サブアプリケーションに転送するすべての要求は、メインアプリケーションのプロキシ·ルートを作成します。プラグインはデフォルトでは、プロキシ経路この種の無効になっています。結果として、私たちの(架空の)WTFormsプラグインは/コンタクト経路に影響を与えますが、/ブログサブアプリケーションのルートには影響ありません。
この動作は、正気のデフォルトとして意図されていますが、オーバーライドすることができます。次の例は、再活性化し、特定のプロキシの経路のすべてのプラグイン:
root.mount('/blog', apps.blog, skip=None)
しかし、思わぬ障害があります:プラグインは単一のルートとして、サブアプリケーション全体を見て、すなわち、プロキシルートは、上記の。サブアプリケーションの個々の経路に影響を与えるためには、明示的にマウントされたアプリケーションにプラグインをインストールする必要があります。
関連リンク
6.テンプレート
テンプレート
ボトルはSimpleTemplateエンジンと呼ばれる高速かつ強力な組み込みのテンプレートエンジンが付属しています。テンプレート()関数またはビュー()デコレータを使用することができますテンプレートをレンダリングします。あなたがしなければならないすべてのテンプレートとは、キーワード引数としてテンプレートに渡したい変数の名前を提供することです。ここでテンプレートをレンダリングする方法の簡単な例は次のとおりです。
@route('/hello') @route('/hello/<name>') def hello(name='World'): return template('hello_template', name=name)
これは、テンプレートファイルhello_template.tplをロードし、名前の変数を設定してそれをレンダリングします。ボトルは。/ views /フォルダまたはbottle.TEMPLATE_PATHリストで指定された任意のフォルダ内のテンプレートを探します。
ビュー()デコレータを使用する代わりに、テンプレート()を呼び出すのテンプレート変数を使用した辞書を返すことができます。
@route('/hello') @route('/hello/<name>') @view('hello_template') def hello(name='World'): return dict(name=name)
構文
テンプレート構文は、Python言語の周りに非常に薄い層である。その主な目的は、ブロックの正しいインデントを確実にするためのものですので、インデントを気にせずに、テンプレートの書式を設定できます。 SimpleTemplateエンジン:完全な構文を記述するためのリンクをクリックしてください
ここでのテンプレートの例は、次のとおりです。
%if name == 'World': Hello {{name}}! This is a test. %else: Hello {{name.title()}}! How are you? %end
キャッシング
テンプレートは、コンパイル後にメモリにキャッシュされます。テンプレートファイルに加えられた変更は、テンプレートのキャッシュをクリアするまでに影響を与えるにはありません。これを行うにはbottle.TEMPLATES.clear()をコールします。キャッシングは、デバッグモードでは無効になっています。
関連リンク
5.データリクエスト
データリクエスト
ボトルには、Cookie、ヘッダーとグローバル·リクエスト·オブジェクトを介してPOSTフォームデータなどのHTTP関連のメタデータへのアクセスを提供します。このオブジェクトは、いつものように長く、それがコールバック関数内からアクセスされると、現在の要求に関する情報が含まれています。これは、複数の要求が同時に処理され、マルチスレッド環境でも動作します。グローバルオブジェクトはスレッドセーフであることができる方法の詳細については、contextlocal参照してください。
注意
ボトルはFormsDictインスタンスで解析されたHTTPのメタデータのほとんどが格納されます。これらは通常の辞書と同じように動作しますが、いくつかの追加機能があります。辞書内のすべての値が属性として用意されています。これらの仮想属性は、常に値が欠落している場合でも、Unicode文字列を返します。その場合には、文字列は空です。
FormsDictはMultiDictのサブクラスであり、キーごとに複数の値を格納することができます。標準の辞書のアクセス方法は単一の値のみを返しますが、MultiDict.getall()メソッドは、特定のキーのすべての値のリスト(空かもしれません)を返します。
完全なAPIと機能のリストは、APIセクション(要求を参照)で説明されていますが、最も一般的なユースケースと機能はあまりにも、ここでカバーされています。
クッキー
クッキーはFormsDictとしてBaseRequest.cookiesに格納されています。 BaseRequest?.get_cookie()メソッドは、別のセクションで説明したように署名されたクッキーにアクセスすることができます。この例では、単純なクッキー·ベースのビュー·カウンタを示しています。
from bottle import route, request, response @route('/counter') def counter(): count = int( request.cookies.get('counter', '0') ) count += 1 response.set_cookie('counter', str(count)) return 'You visited this page %d times' % count
HTTPヘッダー
クライアントから送信されたすべてのHTTPヘッダー(例えばリファラ、エージェントまたはAccept-Language)をWSGIHeaderDictとBaseRequest.headersを介してアクセス可能に格納されています。 WSGIHeaderDictは、基本的に大文字と小文字を区別しないキーを持つ辞書です。
from bottle import route, request @route('/is_ajax') def is_ajax(): if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return 'This is an AJAX request' else: return 'This is a normal request'
クエリ変数
クエリ文字列(/フォーラムのように?ID = 1&ページ= 5)は一般に、サーバーにキー/値ペアの数が少ないを送信するために使用されています。あなたは、文字列全体を取得するためにこれらの値とBaseRequest.query_string属性にアクセスするBaseRequest.query(FormsDict?)を使用することができます。
from bottle import route, request, response @route('/forum') def display_forum(): forum_id = request.query.id page = request.query.page or '1' return 'Forum ID: %s (page %s)' % (forum_id, page)
フォームデータとファイルのアップロードのPOST
POSTとPUTリクエストのリクエストボディは、さまざまな形式でエンコードされたフォームデータを含めることができます。 BaseRequest?.forms辞書はBaseRequest.filesに格納ファイルのアップロードとBaseRequest.POST一つに両方の辞書を組み合わせて、解析されたテキスト形式のフィールドが含まれています。 3すべてのFormsDictインスタンスで、オンデマンドで作成されます。ファイルのアップロードは、いくつかのメタデータと一緒に特別なcgi.FieldStorageオブジェクトとして保存されます。最後に、BaseRequest?.body経由でファイルライクオブジェクトとして生の身体データにアクセスすることができます。
ここで単純なファイルアップロードフォームの例は、次のとおりです。
ファイルを選択
from bottle import route, request @route('/upload', method='POST') def do_upload(): name = request.forms.name data = request.files.data if name and data and data.file: raw = data.file.read() # This is dangerous for big files filename = data.filename return "Hello %s! You uploaded %s (%d bytes)." % (name, filename, len(raw)) return "You missed a field."
Unicodeに関する問題
Python 2ではすべてのキーと値は、バイト文字列です。あなたは、Unicodeが必要な場合は、FormsDict?.getunicode()を呼び出すか、属性アクセスを介して値をフェッチできます。と失敗した場合は空の文字列を返します。どちらの方法で文字列(UTF8デフォルト)をデコードしてみてください。いいえUnicodeErrorをキャッチする必要はありません。
'Göttingen' # An utf8 string provisionally decoded as ISO-8859-1 by the server >>> request.query.city 'Göttingen' # The same string correctly re-encoded as utf8 by bottle
Python 3ではすべての文字列はUnicodeであるが、HTTPはバイトベースのワイヤプロトコルです。サーバは、それらがアプリケーションに渡される前に何らかの形でバイト列をデコードする必要があります。安全側であるためには、WSGIはISO-8859-1(別名は、latin1)、後で別のエンコーディングを使用して再エンコードすることができます可逆シングルバイトコーデックを示唆している。ボトルは、そのFormsDict.getunicode()と属性アクセスのためではなく、その他のdict-アクセスメソッドに対して行います。これらは、あなたが望むものはおそらくありませんサーバの実装によって提供変わらない値を返します。
>>> request.query['city'] 'Göttingen' # An utf8 string provisionally decoded as ISO-8859-1 by the server >>> request.query.city 'Göttingen' # The same string correctly re-encoded as utf8 by bottle
あなたは正しくデコードされた値(例えばWTForms用)全体の辞書が必要であれば、再エンコードされたコピーを取得しFormsDict.decode()を呼び出すことができます。
WSGI環境
各BaseRequestインスタンスはWSGI環境ディクショナリをラップします。オリジナルはBaseRequest.environに格納されますが、リクエストオブジェクト自体も、辞書のように動作しています。興味深いデータのほとんどは、特別なメソッドや属性を介して公開されていますが、直接WSGI環境変数にアクセスする場合は、そのように行うことができます。
@route('/my_ip') def show_ip(): ip = request.environ.get('REMOTE_ADDR') # or ip = request.get('REMOTE_ADDR') # or ip = request['REMOTE_ADDR'] return "Your IP is: %s" % ip
関連リンク
4.コンテンツの生成
コンテンツを生成する
純粋なWSGIでは、アプリケーションから返されることがありますタイプの範囲は非常に限られている。アプリケーションがiterableの降伏バイト文字列を返す必要があります。あなたは、文字列を返すことがあります(文字列がiterableであるため)が、この原因は、ほとんどのサーバーでは、charがchar型コンテンツを送信する。 Unicode文字列は全く許可されていません。これは非常に実用的ではありません。
ボトルは、はるかに柔軟であり、幅広い種類のをサポートしています。それが可能な場合でも、Content-Lengthヘッダを追加し、自動的にUnicodeエンコードされますので、あなたがする必要はありません。あなたは、アプリケーションのコールバックと、これらはフレームワークによって処理される方法の簡単な説明からの復帰可能性のあるデータ型のリストはどのような次のとおりです。
●辞書
前述したように、Pythonの辞書は(またはそのサブクラス)は、自動的にJSON文字列に変換され、application /jsonに設定するには、Content-Typeヘッダでブラウザに返されます。これは、JSONベースのAPIの実装を容易にします。 JSON以外のデータ形式があまりにもサポートされています。多くを学ぶためのチュートリアル - 出力フィルタを参照してください。
空の文字列は、False、Noneまたは他の非真の値:
これらは0に設定し、Content-Lengthヘッダを持つ空の出力を生成します。
●Unicode文字列
Unicode文字列(またはUnicode文字列を得イテラブル)が自動的にContent-Typeヘッダ(デフォルトでUTF8)で指定されたコーデックでエンコードされたとします(下記参照)、通常のバイト文字列として扱われます。
●バイト文字列
ボトルは、全体として文字列を返します(代わりに各char型を反復処理)、文字列の長さに基づいて、Content-
Lengthヘッダを追加します。バイト文字列のリストが最初に結合されます。彼らはメモリに収まるように大きすぎて成長する可能性があるため、バイト文字列が得られる他のイテラブルは結合されません。 Content-Lengthヘッダは、このケースでは設定されていません。
<●HTTPErrorまたはHttpResponseのインスタンス
これらを返すことは例外として、それらを発生させる場合と同じ効果があります。 HTTPErrorの場合には、エラーハンドラが適用されます。詳細については、エラー·ページを参照してください。
●ファイルオブジェクト
。read()メソッドを持っている物は全て、ファイルまたはファイルのようなオブジェクトとして扱われ、WSGIサーバのフレームワークによって定義された呼び出し可能なwsgi.file_wrapperに渡されます。いくつかのWSGIサーバ実装はより効率的にファイルを送信するために最適化されたシステムコールの使用(sendfileを)作ることができます。他のケースで、これは単にメモリに収まるチャンクを反復します。このようにContent-LengthやContent-Typeなどのオプションのヘッダーは自動的に設定されていません。可能であれば、send_file()を使用します。詳細については、静的なファイルを参照してください。
イテラブルとジェネレータ
あなたは、あなたのコールバック内で収量を使用するか、または反復可能な、iterableの利回りバイト文字列に限り、Unicode文字列、またはHTTPErrorのHttpResponseインスタンスを返すことが許可されています。入れ子になったイテラブルは申し訳ありませんが、サポートされていません。 HTTPステータスコードとヘッダが、すぐにiterableの利回りとしての最初の非空の値をブラウザに送信されることに注意してください。これらを変更すると、後で効果がありません。
このリストの順序は重要である。次の例のようにread()メソッドでstrのサブクラスを返すことがあります。文字列が最初に処理されるため、それはまだ、代わりにファイルの文字列として扱われます。
デフォルトエンコーディングを変更する
ボトルは、Unicode文字列をエンコードする方法を決定するためにContent-Typeヘッダのcharsetパラメータを使用しています。 text / htmlに、このヘッダーのデフォルト値は、直接Response.Charsetを属性を設定することのcharset = UTF8とResponse.content_type属性を使用するか、または変更することができます。 (Responseオブジェクトは、セクションレスポンスオブジェクトに記述されています。)
ボトルのインポート応答から
from bottle import response @route('/iso') def get_iso(): response.charset = 'ISO-8859-15' return u'This will be sent with ISO-8859-15 encoding.' @route('/latin9') def get_latin(): response.content_type = 'text/html; charset=latin9' return u'ISO-8859-15 is also known as latin9.'
まれに、Pythonのエンコーディング名は、HTTP仕様でサポートされている名前とは異なります。次に、両方を行う必要があります:最初のResponse.content_typeヘッダを(変更せずにクライアントに送られます)を設定してから、(Unicodeをエンコードするために使用されている)Response.Charsetを属性を設定します。
静的ファイル
直接ファイルオブジェクトを返すことができますが、static_file()は静的ファイルを提供するための推奨方法です。自動的に推測するのmime-typeは、Last-Modifiedヘッダを追加し、セキュリティ上の理由から、ルートディレクトリへのパスを制限し、適切なエラー応答(許可エラーの401、行方不明のファイルの404)を生成します。それも、If-Modified-Sinceヘッダーをサポートしており、最終的に304 Not Modifiedを応答を生成します。あなたは、推測を無効にするには、カスタムのMIMEタイプを渡すことができます。
from bottle import response @route('/iso') def get_iso(): response.charset = 'ISO-8859-15' return u'This will be sent with ISO-8859-15 encoding.' @route('/latin9') def get_latin(): response.content_type = 'text/html; charset=latin9' return u'ISO-8859-15 is also known as latin9.'
あなたが本当にする必要があれば、例外としてstatic_file()の戻り値を上げることができます。
強制ダウンロード
ほとんどのブラウザはMIMEタイプが知られているアプリケーション(例えばPDFファイル)に割り当てられている場合、ダウンロードしたファイルを開こうとします。これはあなたが望むものでない場合は、ダウンロードダイアログを強制的にでもユーザーにはファイル名を示唆することができます。
@route('/download/<filename:path>') def download(filename): return static_file(filename, root='/path/to/static/files', download=filename)
ダウンロードパラメータが単にTrueの場合、元のファイル名が使用されます。
HTTPエラーとリダイレクト
abort()関数は、HTTPエラーページを生成するためのショートカットです。
from bottle import route, abort @route('/restricted') def restricted(): abort(401, "Sorry, access denied.")
別のURLにクライアントをリダイレクトするには、新しいURLに設定するLocationヘッダを持つ他の応答を参照してください303を送信することができます。あなたのためにそれをしない)(リダイレクトします。
from bottle import redirect @route('/wrong/url') def wrong(): redirect("/right/url")
あなたは番目のパラメータとして別のHTTPステータスコードを提供することがあります。
注意
どちらの関数もHTTPError例外を発生させることによって、コールバックコードが中断されます。
その他の例外
HTTPResponseはまたはHTTPError以外のすべての例外は、500内部サーバーエラー応答になりますので、彼らはあなたのWSGIサーバがクラッシュすることはありません。あなたがbottle.app().catchallにFalseを設定することで、ミドルウェアで例外を処理するために、この動作をオフにすることができます。
レスポンスオブジェクト
そのようなHTTPステータスコード、レスポンスヘッダおよびクッキーのような応答メタデータは、それがブラウザに送信される点に対応し、最大と呼ばれるオブジェクトに格納されています。直接これらのメタデータを操作したり、これを行うには事前に定義されたヘルパー·メソッドを使用することができます。完全なAPIと機能のリストは、APIセクション(応答を参照)で説明されていますが、最も一般的なユースケースと機能はあまりにも、ここでカバーされています。
状態コード
HTTPステータスコードが200 OK、ブラウザとデフォルトの動作を制御します。ほとんどのシナリオでは、手動でのResponse.Status属性を設定する必要がありますが、中止()ヘルパーを使用するか、または適切なステータスコードを使用してHTTPResponseインスタンスを返しません。任意の整数は許可されているが、HTTP仕様で定義されている以外のコードは、ブラウザのみ、ブレーク基準を混乱させます。
レスポンスヘッダ
のCache-Controlや場所などのレスポンスヘッダがResponse.set_header()を介して定義されています。このメソッドは二つのパラメータ、ヘッダ名と値をとります。名前の部分は大文字小文字を区別しません:
@route('/wiki/<page>') def wiki(page): response.set_header('Content-Language', 'en') ...
ほとんどのヘッダには、名前ごとに1つのヘッダがクライアントに送信であることを意味し、ユニークです。いくつかの特別なヘッダは、しかし、応答で複数回表示されるように許可されています。追加のヘッダーを追加するには、代わりにResponse.set_header()のResponse.add_header()を使用します。
response.set_header('Set-Cookie', 'name=value') response.add_header('Set-Cookie', 'name2=value2')
これは単なる例であることに注意してください。あなたはクッキーを操作する場合は、このまま読み進めてください。
クッキー
クッキーは、ユーザーのブラウザプロファイルに格納されているテキストの名前部分です。あなたがRequest.get_cookie()を介して以前に定義されたクッキーにアクセスし、Response.set_cookie()で新しいクッキーを設定することができます。
@route('/hello') def hello_again(): if request.get_cookie("visited"): return "Welcome back! Nice to see you again" else: response.set_cookie("visited", "yes") return "Hello there! Nice to meet you"
Response.set_cookie()メソッドは、クッキーの寿命と動作を制御する追加のキーワード引数の数を受け入れます。最も一般的な設定のいくつかはここで説明されています。
max_age:最大年の秒数表記。 (デフォルト:なし)
expires: datetimeオブジェクトまたはUNIXタイムスタンプ。 (デフォルト:なし)
domain:クッキーの読み取りを許可されているドメイン。 (デフォルト:現在のドメイン)
path:指定されたパスにCookieを制限する(デフォルト:/)
secure: (デフォルト:オフ)HTTPS接続にクッキーを制限します。
httponly:このCookieは読むために、クライアント側のJavaScriptを防止する(デフォルト:オフとは、Python2.6またはそれ以降が必要です)。
どちらもも保つように設定されている有効期限が切れた場合、クッキーは、ブラウザセッションの終了時または、すぐにブラウザのウィンドウが閉じられるように有効期限が切れます。クッキーを使用する際に考慮すべきいくつかの他の落とし穴があります。
クッキーはほとんどのブラウザ内のテキストの4キロバイトに制限されています。 一部のユーザーがすべてではクッキーを受け入れないようにブラウザを設定します。ほとんどの検索エンジンにもクッキーを無視します。アプリケーションはまだクッキーなしで動作することを確認します。 クッキーは、クライアント側で保存され、どのような方法で暗号化されていません。クッキーに保存何であれ、ユーザがそれを読むことができます。それよりもさらに悪いことに、攻撃者があなたの側にXSSの脆弱性を介してユーザーのcookieを盗むことができるかもしれません。一部のウイルスはあまりにも、ブラウザのCookieを読み取ることが知られています。したがって、クッキーに機密情報を格納することはありません。 クッキーは簡単に悪意のあるクライアントによって偽造されています。クッキーを信用してはいけません。
署名されたクッキー
上述したように、クッキーは簡単に悪意のあるクライアントによって偽造されています。ボトルは、暗号化操作のこの種のを防ぐために、クッキーに署名することができます。あなたがしなければならないすべては、読み取りまたは設定するクッキーを、そのキー秘密にするたびに秘密のキーワード引数を介して署名キーを提供することです。クッキーが署名されていないか、または署名鍵が一致しない場合、結果として、Request.get_cookie()はNoneを返します。
@route('/login') def login(): username = request.forms.get('username') password = request.forms.get('password') if check_user_credentials(username, password): response.set_cookie("account", username, secret='some-secret-key') return "Welcome %s! You are now logged in." % username else: return "Login failed." @route('/restricted') def restricted_area(): username = request.get_cookie("account", secret='some-secret-key') if username: return "Hello %s. Welcome back." % username else: return "You are not logged in. Access denied."
自動的に加えて、ボトルをpickleあるいはunpickle署名クッキーに保存されているすべてのデータ。これは、ピクルスのデータが4 KBの制限を超えない限り、クッキーへのピクルス可能なオブジェクトを(だけでなく、文字列)、保存することができます。
警告
署名されたクッキーは暗号化されていません(クライアントはコンテンツを見ることができます)と(クライアントが古いクッキーを復元することができます)コピーして保護されていない。主な目的は、安全のpickle化およびunpickle化すると、クライアント側で秘密情報を格納するのではなく、操作を防ぐためです。
関連リンク
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つのことを示しています。
ということです。
ダイナミックルート
単純なワイルドカードは、角括弧で囲まれた名前(例えば <name> 、<filename>)から成り、スラッシュに1つまたは複数の文字を受け入れます。
ワイルドカードを含むルートがダイナミックルートで呼ばれる(静的ルートとは対照的に)と、同時に複数のURLと一致します。
たとえば、/hello/
各ワイルドカードはリクエストのコールバック関数へキーワード引数として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が生じるなどではエラーハンドラは起動しません。
関連リンク
2.クイックスタート
クイックスタート: "Hello World"
このチュートリアルでは、ボトルをインストール、またはプロジェクトディレクトリにコピーしたことを前提にしています。最も基本的な "Hello World"の例から始めましょう:
from bottle import route, run @route('/hello') def hello(): return "Hello World!" run(host='localhost', port=8080, debug=True)
このスクリプトを実行し、http://localhost:8080/helloにアクセスすると、あなたのブラウザに "Hello World!"が表示されます。
route() デコレータは、URLパスのコードの一部をバインドします。この場合では、hello() 関数に/hello というURLをリンクします。
これは、ルート(すなわちデコレータ名)と呼ばれ、このフレームワークの中で最も重要な概念です。必要な数だけルートとして定義することができます。ブラウザがURLを要求するたびに、リンクされた関数(今回はhello())が呼び出され、戻り値がブラウザに返されます。それほどシンプルなのです。
最後の行のrun()で組み込みの開発用(テスト用)サーバーを起動します。これは、localhostのポート8080上で実行され、Control-cを押すまで、リクエストを提供します。
後でサーバーのバックエンドを切り替えることができますが、この開発用サーバ上に必要とする全てが入っている。開発用サーバーは設定を全く必要とせず、あなたのアプリケーションを取得し、ローカルテストのために実行する上で信じられないほど労力を必要としない方法です。
デバッグ·モードは初期の開発時に非常に便利ですが、公開されたアプリケーションのためにオフにする必要があります。このことを念頭に置いて保管してください。
これは非常に単純な例ですが、bottleを使ったアプリケーション構築の基本的な考え方を含んでいます。さらに読み続けて、他に何が出来るのかを見ていきましょう。
デフォルトのアプリケーション
簡単のためにこのチュートリアルではほとんどの例で、ルートを定義するモジュールレベルのデコレータであるroute()を使います。
これはルートを追加するための標準的なデフォルトアプリケーションであり、初めてroute()を呼び出した時に自動的に作成されるbottle.Bottleのインスタンスです。他のいくつかのモジュールレベルのデコレータと関数は、このデフォルトのアプリケーションに関連していますが、より多くのオブジェクト指向のアプローチを好むと余分なタイピングを気にしない場合は、別のアプリケーションオブジェクトを作成し、グローバルなものの代わりにそのを使用することができます。
from bottle import Bottle, run app = Bottle() @app.route('/hello') def hello(): return "Hello World!" run(app, host='localhost', port=8080)
オブジェクト指向のアプローチは、さらにデフォルトのアプリケーションのセクションで説明されています。ちょうどあなたが選択を持っていることを心に留めておいてください。
関連リンク
1.Bottleのインストール
インストール
ボトルは、任意の外部ライブラリに依存しません。あなたは自分のプロジェクトディレクトリにbottle.pyダウンロードしてコーディングを開始することができます。
$ wget http://bottlepy.org/bottle.py
これにより、すべての新機能が含まれている最新の開発スナップショット(不安定版)を取得します。
より安定した環境を好む場合には、安定版を取得するべきです。安定版はPyPi上で取得でき、pip(推奨)やeasy_install、またはあなたのOSのパッケージマネージャを介してインストールすることができます。
$ sudo pip install bottle # 推奨 $ sudo easy_install bottle # pip無しにやる方法 $ sudo apt-get install python-bottle # Debian, Ubuntuなどのそれぞれのパッケージ管理システム
[パッケージシステム全体を、インストール権限を持っていないまたは作成したくない場合]
$ virtualenv develop # 仮想環境を作成 $ source develop/bin/activate # デフォルトのPythonを仮想環境上のものに変更 (develop)$ pip install -U bottle # 仮想環境上にbottleをインストール
[virtualenvをお使いのシステムにインストールされていない場合]
$ wget https://raw.github.com/pypa/virtualenv/master/virtualenv.py #virtualenvをwget $ python virtualenv.py develop # 仮想環境を作成 $ source develop/bin/activate # デフォルトのPythonを仮想環境上のものに変更 (develop)$ pip install -U bottle # 仮想環境上にbottleをインストール
いずれの方法でも、bottleのアプリケーションを実行するためにはPython 2.5以降(3.xを含む)が必要です。
関連リンク