9.展開と用語集
展開
ボトルは、デフォルトでは、組み込みのwsgirefのWSGIServerで実行されます。この非スレッドHTTPサーバは、開発と早期の生産のための完全にいいのですが、ときに、サーバーの負荷が増大するパフォーマンスのボトルネックになる可能性があります。
パフォーマンスを向上させる最も簡単な方法は、ペーストまたはCherryPyにのようなマルチスレッド·サーバー·ライブラリをインストールし、その代わりに、シングルスレッド·サーバの使用するように瓶を伝えることです。
bottle.run(server='paste')
展開:これは、多くの他のデプロイメント·オプションは、別の資料に記載されている
用語集
callback
いくつかの外部アクションが発生したときに呼び出されることであるプログラマのコード。 Webフレームワークのコンテキストでは、URLのパスとアプリケーションコードの間のマッピングは、多くの場合、URLごとにコールバック関数を指定することで実現されています。
decorator
別の関数を返す関数は、通常@デコレータ構文を使用して関数変換として適用されます。デコレータについての詳細は、関数定義のためにPythonのドキュメントを参照してください。
environ
ルートの下のすべてのドキュメントに関する情報が保存され、相互参照に使用される構造。環境は、連続失点だけで新機能と変更されたドキュメントを読み、解析する必要がありますので、解析ステージの後のピクルスです。
handler function
いくつかの特定のイベントや状況を処理する関数。 Webフレームワークでは、アプリケーションはアプリケーションを構成するそれぞれの特定のURLのコールバックとしてハンドラ関数をアタッチすることにより開発されています。
source directory
そのサブディレクトリを含むディレクトリは、1つのスフィンクスプロジェクトのすべてのソースファイルが含まれています。
関連リンク
8.開発
開発
基礎を学び、独自のアプリケーションを書きたいかと思った時?あなたがより生産的に、役立つかもしれないいくつかのヒントはここにある。
デフォルトのアプリケーション
ボトルはボトルインスタンスのグローバル·スタックを維持し、モジュールレベルの関数やデコレータのいくつかの既定値としてスタックの一番上を使用しています。ルート()デコレータは、例えば、デフォルトのアプリケーションにBottle.route()を呼び出すためのショートカットです。
@route('/') def hello(): return 'Hello World'
この小さなアプリケーションのために非常に便利で、あなたにいくつかの入力を保存しますが、また、モジュールがインポートされるとすぐに、ルートがグローバルなアプリケーションにインストールされている、ことを意味します。インポートこの種の副作用を避けるために、ボトルには、アプリケーションを構築するための第二に、より明示的な方法を提供しています。
app = Bottle() @app.route('/') def hello(): return 'Hello World'
アプリケーションオブジェクトを分離することも、再利用性、多くを向上させます。他の開発者が安全にモジュールからアプリケーションオブジェクトをインポートして、一緒にアプリケーションをマージするBottle.mount()を使用することができます。
代替手段として、あなたはまだ便利なショートカットを使用している間に経路を分離するためにアプリケーション·スタックの使用を行うことができます。
default_app.push() @route('/') def hello(): return 'Hello World' app = default_app.pop()
アプリ()とdefault_app()の両方がAppStackのインスタンスであるとスタックのようなAPIを実装しています。あなたから、必要に応じてスタックにプッシュアプリケーションとポップすることができます。あなたが別のアプリケーションオブジェクトを提供していないサードパーティ製のモジュールをインポートしたい場合にも役立ちます。
default_app.push()
import some.module
app = default_app.pop()
モードのデバッグ
初期の開発中に、デバッグモードでは、非常に役立つことがあります。
bottle.debug(True)
このモードでは、ボトルははるかに冗長であり、エラーが発生するたびに有用なデバッグ情報を提供します。それはまたあなたの邪魔になるかもしれないいくつかの最適化を無効にし、可能な限り設定ミスについて警告をいくつかのチェックを追加します。
ここで、デバッグモードで変更するものの不完全なリストは、次のとおりです。
デフォルトのエラーページでは、トレースを示しています。
テンプレートはキャッシュされません。
プラグインはすぐに適用されます。
ただ本番サーバー上でデバッグモードを使用しないことを確認してください。
リロードオート
開発中に、あなたの最近の変更をテストするためにサーバーをたくさん起動する必要があります。自動リロード機能はあなたのためにこれを行うことができます。あなたは、モジュールファイルを編集するたびに、リロード機能·サーバー·プロセスが再起動し、コードの最新バージョンをロードします。
from bottle import run run(reloader=True)
どのように動作します:メインプロセスはサーバを起動するが、メインプロセスを開始するために使用されているのと同じコマンドライン引数を使用して、新しい子プロセスを生成しません。すべてのモジュールレベルのコードは、少なくとも二回実行されます!注意してください。
子プロセスはos.environ BOTTLE_CHILD?がtrueに設定されていると通常の非リロードアプリケーションサーバとして起動します。すぐにロードされたモジュールの変更のいずれかと同様に、子プロセスは終了し、メイン·プロセスによって再生成された。テンプレートファイルの変更は、リロードをトリガしません。テンプレートキャッシュを無効にするデバッグモードを使用してください。
リロードは、子プロセスを停止する能力に依存している。あなたがsignal.SIGINTをサポートしていないWindowsまたは他のオペレーティングシステム(PythonでとKeyboardInterruptが発生している)上で実行されている場合、signal.SIGTERMは子を殺すために使用されています。終了ハンドラやfinally節などは、SIGTERM後に実行されないことに注意してください。
コマンドラインインタフェース
バージョン0.10以降では、コマンドラインツールとしてボトルを使用することができます。
$ python -m bottle Usage: bottle.py [options] package.module:app Options: -h, --help show this help message and exit --version show version number. -b ADDRESS, --bind=ADDRESS bind socket to ADDRESS. -s SERVER, --server=SERVER use SERVER as backend. -p PLUGIN, --plugin=PLUGIN install additional plugin/s. --debug start server in debug mode. --reload auto-reload on file changes.
localhostにポートのペアとデフォルト値:8080 Addressフィールドは、IPアドレスまたはIPアドレスを取ります。他のパラメータは自己説明的であるべきです。
プラグインとアプリケーションの両方がインポート式を介して指定されています。これらは、コロンで区切られ、そのモジュールの名前空間に評価するのimportパス(例えばpackage.module)および式から構成されています。詳細については、load()を参照してください。ここではいくつかの例は、次のとおりです。
# Grab the 'app' object from the 'myapp.controller' module and # start a paste server on port 80 on all interfaces. python -m bottle -server paste -bind 0.0.0.0:80 myapp.controller:app # Start a self-reloading development server and serve the global # default application. The routes are defined in 'test.py' python -m bottle --debug --reload test # Install a custom debug plugin with some parameters python -m bottle --debug --reload --plugin 'utils:DebugPlugin(exc=True)'' test # Serve an application that is created with 'myapp.controller.make_app()' # on demand. python -m bottle 'myapp.controller:make_app()''
関連リンク
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が生じるなどではエラーハンドラは起動しません。
関連リンク