読者です 読者をやめる 読者になる 読者になる

たけし備忘録

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

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化すると、クライアント側で秘密情報を格納するのではなく、操作を防ぐためです。

次の記事へ進む → 5.データリクエスト


関連リンク