「伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門」を更新しました

6 min read読了の目安(約5400字

本を更新しました

チャプター「POSTパラメータを扱えるようにする」 を更新しました。

続きを読みたい方は、ぜひBookの「いいね」か「筆者フォロー」をお願いします ;-)


以下、書籍の内容の抜粋です。


リクエストボディを扱う

前章の最後に、Chromeで/show_requestへアクセスした結果を見てみるとリクエストボディが空になっていたことが分かりました。

しかし、仮にボディが空でなかったとして、私達のWebアプリケーションはリクエストボディを変換したり解釈したりする処理はまだないのでした。
せっかくなので、ここいらでリクエストボディを扱えるようにしておきましょう。

リクエストボディはクライアントからサーバーへ付加的な情報(パラメータとも言う)を送るのに用いられ、一例としてPOSTメソッドのリクエスト(以下、POSTリクエスト)などで使われます。

本章では、POSTメソッドのパラメータに関する処理を実装することで、リクエストボディの取り扱いについて学びましょう。

POSTリクエストを送信し、ボディを観察してみる。

アレコレと説明する前に、まずはリクエストボディが実際にどのように使われているのか観察するところから始めましょう。

POSTリクエストを送信する

POSTリクエストをブラウザがどのようなときに送るかというと、代表的なのは<form>タグを用いて作られたフォームのsubmitボタンが押された時です。

実際にフォームを含むHTMLを作成し、実験してみましょう。

ソースコード

study/static/form.html

https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter15/static/form.html

study/workerthread.py

https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter15/workerthread.py#L169

解説

study/static/form.html

HTMLファイルを新規に作成してください。

内容は初歩的なHTMLで、詳しく説明する必要はないでしょう。
1つの<form>タグの中に、テキストボックスやプルダウン、セレクトボックスなど、色々な種類の入力フォームが入っているだけです。

staticディレクトリ内に作成しなければいけないので注意してください。

study/workerthread.py

22, 67, 92, 112行目

        "html": "text/html; charset=UTF-8",
                    content_type = "text/html; charset=UTF-8"

Content-Typeを少し変更しています。

Content-Typeヘッダーには文字列のエンコーディングを指定することができ、ブラウザで日本語を表示させるためには日本語に対応したエンコーディングの指定が必要になります。

エンコーディングについては話が込み入ってしまいますので、ピンと来ない方はおなじないだと思って追記しておいてください。

このファイルは staticディレクトリの中に入っており静的ファイル配信の対象となります ので、サーバーを起動した状態でChromeから http://localhost:8080/form.html へアクセスすると表示することができます。


ブラウザは、type="submit"の要素(以下、submitボタンと呼びます)がクリックされると、<form>タグのaction属性で指定されたURLへPOSTリクエストを送信します。

action属性で指定するURLについて、ホストやポートを省略すると、現在開いているページと同じホスト/ポートへ送信されます。
つまり、今回のように

  • 現在開いているページが http://localhost:8080/form.html である
  • <form action="/show_request">である

という場合では、POSTリクエストはhttp://localhost:8080/show_request へ送信されます。


では、下図のようにフォームに値を入力して、送信ボタンを押してみましょう。

先程説明した通り、このフォームの入力内容は、POSTリクエスト/show_requestへ送られます。
/show_requestは前章でHTTPリクエストの内容が表示されるようにしておいたはずですので、これでPOSTリクエストの内容が見れるだろう、という算段です。

実際、送信ボタンを押すと、次の画面で下記のように表示されるはずです。

単にURLバーに/show_requestと入力してページ遷移した場合と違って、リクエストボディに値が含まれていることが分かります。

また、

Content-Type: application/x-www-form-urlencoded

というヘッダーも新たに追加されていることにも注目しておいてください。
後ほど、このヘッダーが大きな意味を持つことを説明します。

POSTリクエストのボディを観察する

さて、POSTリクエストのボディの具体的な中身が見れたことですので、観察してお勉強していきましょう。
リクエストボディを見てみると、テキストボックスパスワードなどの個々の入力フォームの値が決まったフォーマットで連結されて渡されてきているのが分かります。

そのフォーマットとは、1つの入力フォームに対して
[HTML要素のname属性の値]=[フォームに入力された値]
というペアがあり、別々のフォームの値同士は&で連結されているようなフォーマットです。

また、半角スペース+という記号に置き換えられ、改行コード日本語%で始まる謎の文字列 に変換されていることが分かります。
(日本語の入力値は、hidden_valueの値を見てください)

また、<select>要素のように複数選択を許可する入力フォームでは、同じnameで複数の値が送られてきているようです。
例)check_name=check2&check_name=check3

また、アップロードしたファイルを見てみるとファイル名だけしか送られておらず、ファイルの内容は送信されていません。

POSTパラメータのフォーマットについて

POSTリクエストで送りたいデータ(以下、POSTパラメータ)をリクエストボディを使ってサーバーへ送る際、どのようなフォーマットで送るかは重要です。
ここでフォーマットと言っているのは、リクエストボディの中でパラメータのnamevalueを表すのになんの記号を使うのか、複数のデータを分けるのになんの記号を使うのか、マルチバイト文字をどのように表現するのか、画像ファイルのようなバイナリデータをどのように表現するのか、などです。

フォーマットは様々な種類が考えられますが、このフォーマット方式の認識がクライアント側とサーバー側で違うと、送ったパラメータをサーバ側で正しく認識できません。
(クライアント側は=という記号は「namevalueを分ける記号」だと思って使っているのに、サーバー側ではこれを「改行コード」だと思って解釈してしまうと、訳の分からない事になってしまうわけです。)

そこで、POSTリクエストを送る時は、必ずリクエストボディのフォーマットを示すContent-Typeというヘッダーをつけてフォーマットを明示してあげる必要があります。

今回でいうと、

Content-Type: application/x-www-form-urlencoded

がそれに当たるというわけです。

以下では、よく使われるフォーマット(Content-Type)について3つ紹介しておきます。

application/x-www-form-urlencoded

こちらは、ブラウザが<form>タグでenctype属性を指定しなかった場合に使われるデフォルトのフォーマットです。
別名「URLエンコーディング」や「パーセントエンコーディング」とも呼ばれ、URLとして利用可能な文字のみを使って様々なデータを表せるようにフォーマットが決められています。

既にさきほど見た通り、

  1. 項目のnamevalue=で連結する
  2. 複数の項目を送る際は&で連結する
  3. 半角スペースは+を使う
  4. その他のURLに使えない文字は、UTF-8で符号化した上で、そのバイト列を%XXで表す
  5. UTF-8で符号化できないバイナリデータは扱えない(ファイルアップロージ時、ファイルの中身は送信しない)

などが特徴です。

multipart/form-data

こちらは、<form enctype="multipart/form-data">のように、enctype属性で明示的に指定することで利用できます。
説明する前に実際に中身を見てみましょう。

さきほど作成したform.html<form>要素に、enctypeを指定して、フォームを送信してみてください。

ファイルアップロードした時の表示を確認したい場合は、ChromeではなくFirefoxというブラウザを使い、小さいデータ(数KBの画像など)を送るようにしてください。

ChromeやSafariは、ファイルデータを送る際にkeep-aliveを使って複数回に分けてリクエストを送信する挙動となっていますが、私たちのWebサーバーはkeep-aliveに対応しておらずデータを正常に受け取れないためです。

Firefoxは小さいデータであれば1リクエストで送ってきますので、正常にデータを受け取ることができます。


下記ではFirefoxで挙動確認した画面を掲載しますが、Firefoxをインストールするのが面倒な方は、Chromeでファイルは アップロードせずに 動作確認してみてください。
テキストフォームなど、ファイル以外のフォームであればデータが受け取れるはずです。

まず最初に注目するのは、フォームデータの各項目が特殊なセパレータによって分割されている点です。
(今回でいうとセパレータは---------------------------10847194838586372301567045317)


続きはBookで!

チャプター「POSTパラメータを扱えるようにする」