Python + Bottleで、フォームやCookieに日本語を使ったら文字化けしたため、メモを残します。
目次
環境
- Python 3.6.1
- Bottle 0.12.13
フォームやCookieに設定した値の取得について
フォームやCookieに設定した値は
- フォームに入力した値:
request.forms
- Cookieに設定した値:
request.cookies
という、FormsDict
型のオブジェクトとして保存されています。
例えば、
<form action="/" method="POST"> <div> <label for="input">入力</label> <input type="text" name="input" size="60"> </div> <div> <input type="submit"> <a href="/delete_cookie">Cookie削除</a> </div> </form>
というフォームがあった場合、
from bottle import Bottle, request app = Bottle() @app.post('/') def post_form(): result = request.forms.get('input')
として値を取得します。
また、Cookieの場合は、
response.set_cookie('key', 'value')
でCookieに値を設定し、
cookie_by_get = request.get_cookie('key')
で値を取得します。
日本語の文字化けと対応について
ただ、上記のrequest.forms.get()
やrequest.get_cookie()
では、日本語などのマルチバイト文字の場合に文字化けします。
result = request.forms.get('input') print(result) #=> ã
BottleのチュートリアルのNoteに、原因の記載があります。
In Python 3 all strings are unicode, but HTTP is a byte-based wire protocol. The server has to decode the byte strings somehow before they are passed to the application. To be on the safe side, WSGI suggests ISO-8859-1 (aka latin1), a reversible single-byte codec that can be re-encoded with a different encoding later. Bottle does that for FormsDict.getunicode() and attribute access, but not for the dict-access methods
request.forms.get(key)
やrequest.forms[key]
では、latin1
でデコードした値となるため、文字化けしているようです。
latin1でのデコードについては、PEP3333(日本語訳)のこのあたりで触れられています。
Unicode の問題 | PEP 3333: Python Web Server Gateway Interface v1.0.1 — knzm.readthedocs.org 2012-12-31 documentation
そのため、utf-8
でデコードした値を取得するには、
request.forms.getunicode(key)
request.forms.<key>
- 名前がkeyである属性へアクセス
request.forms.decode().get(key)
- decode()メソッドでデコードしてから、get()する
- FormsDict.decode() | API Reference — Bottle 0.13-dev documentation
request.forms.decode().getall(key)
などを使います。
INTRODUCING FORMSDICT | Tutorial — Bottle 0.13-dev documentation
フォームの値について、
# 値を取得 form_by_get = request.forms.get('input') form_by_dict_key = request.forms['input'] form_by_getunicode = request.forms.getunicode('input') form_by_attr = request.forms.input form_by_decode = request.forms.decode().get('input') form_by_getall = request.forms.getall('input') form_by_getall_first = request.forms.getall('input')[0] form_by_decode_getall = request.forms.decode().getall('input') form_by_decode_getall_first = request.forms.decode().getall('input')[0] # テンプレートへ反映 return jinja2_template( 'form.html', form_by_get=form_by_get, form_by_dict_key=form_by_dict_key, form_by_getunicode=form_by_getunicode, form_by_attr=form_by_attr, form_by_decode=form_by_decode, form_by_getall=form_by_getall, form_by_getall_first=form_by_getall_first, form_by_decode_getall=form_by_decode_getall, form_by_decode_getall_first=form_by_decode_getall_first)
としてブラウザで確認したところ、
となりました。
また、Cookieの場合も、POSTで
response.set_cookie('input', request.forms.get('input'))
と値を設定してから、GETで
cookie_by_get = request.get_cookie('input', '') cookie_by_dict_key = request.cookies['input'] if request.cookies else '' cookie_by_getunicode = request.cookies.getunicode('input', default='') cookie_by_attr = request.cookies.input if request.cookies else '' cookie_by_decode = request.cookies.decode().get('input', '')
と値を取得してブラウザで表示したところ、
となりました。
その他
以前、WebTestのサンプルで、Bottleのフォームを使った時に文字化けしていました。
Pythonで、WebTestを使って、WSGIサーバを起動せずにWSGIアプリのテストをする - メモ的な思考的な
当時、文字化けの原因がつかめませんでしたが、POSTされたフォームの値をget()
で取得していたのが原因でした。
そのため、上記のサンプルはget()
ではなく、getunicode()
を使うように書き換えました。
ソースコード
GitHubに上げました。e.g._FormsDict_using_multi_byte_string
ディレクトリ以下が今回のサンプルです。
thinkAmi-sandbox/Bottle-sample: Bottle (python web framework) sample codes