Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19624] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージ内の request.go ファイルに対する変更です。このファイルは、HTTPリクエストの処理、特にフォームデータの解析とアクセスに関連する機能を提供します。具体的には、http.Request 型のメソッドである FormValue および PostFormValue の挙動が修正されています。

コミット

net/http: [Post]FormValue ignores parse errors

LGTM=r
R=golang-codereviews, bradfitz, r
CC=golang-codereviews
https://golang.org/cl/102640046

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/128eed2749fa1bc38b3b605c9749441a009ac791

元コミット内容

このコミットの目的は、net/http パッケージの Request.FormValue および Request.PostFormValue メソッドが、内部で呼び出すフォーム解析関数(ParseFormParseMultipartForm)によって返されるエラーを無視するように変更することです。

変更の背景

Goの net/http パッケージにおいて、HTTPリクエストのフォームデータにアクセスするための主要なメソッドとして Request.FormValueRequest.PostFormValue があります。これらのメソッドは、必要に応じてリクエストボディやURLクエリパラメータを解析するために、内部的に ParseFormParseMultipartForm といった関数を呼び出します。

ParseFormParseMultipartForm は、リクエストのフォームデータが不正な形式である場合や、マルチパートフォームの処理中にエラーが発生した場合(例えば、メモリ制限を超過した場合など)にエラーを返します。

このコミット以前の FormValue および PostFormValue の実装では、これらの内部的な解析関数がエラーを返した場合、そのエラーがどのように扱われるかが明確ではありませんでした。しかし、多くの場合、開発者は FormValuePostFormValue を使用する際に、フォームデータの解析エラーを直接処理することを期待していません。これらのメソッドは、特定のキーに対応する値を取得する「便利な」関数として利用されることが多く、解析エラーが発生したとしても、利用可能な部分的なデータを取得できることを望む場合があります。

この変更の背景には、以下のような考慮事項があったと考えられます。

  1. 利便性の向上: FormValuePostFormValue は、特定のフォームフィールドの値に簡単にアクセスするためのものです。これらの関数が内部的な解析エラーによって値を返さない、あるいは予期せぬ挙動をする場合、開発者はより複雑なエラーハンドリングを強いられることになります。エラーを無視することで、これらの関数の利便性が向上します。
  2. 一般的なユースケースへの対応: 多くのWebアプリケーションでは、フォームデータの一部が不正であっても、他の有効なデータは処理を続行したい場合があります。例えば、ユーザーがアップロードしたファイルが大きすぎるためにマルチパートフォームの解析が失敗しても、他のテキストフィールドの値は取得したい、といったケースです。
  3. エラー処理の分離: フォーム解析のエラーは、より低レベルな問題(ネットワークの問題、不正なデータ形式など)に起因することが多いです。FormValuePostFormValue のような高レベルなAPIでこれらのエラーを直接処理させるのではなく、必要であれば ParseFormParseMultipartForm を直接呼び出してエラーを明示的に処理するという選択肢を開発者に残すことで、エラー処理の責任を分離できます。

このコミットは、FormValuePostFormValue が、内部的なフォーム解析エラーを透過的に無視し、可能な限り値を返すという、より実用的な挙動を明確にするために行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびHTTPプロトコルに関する知識が必要です。

  1. Go言語の net/http パッケージ:

    • Go言語でHTTPクライアントおよびサーバーを構築するための標準ライブラリです。
    • http.Request 構造体は、受信したHTTPリクエストのすべての情報(メソッド、URL、ヘッダー、ボディなど)をカプセル化します。
    • http.ResponseWriter インターフェースは、HTTPレスポンスを構築するために使用されます。
  2. HTTPフォームデータ:

    • Webフォームを通じて送信されるデータは、主に以下の2つの形式でエンコードされます。
      • application/x-www-form-urlencoded: キーと値のペアが key1=value1&key2=value2 の形式でURLエンコードされて送信されます。これはGETリクエストのクエリパラメータや、POSTリクエストのボディでよく使われます。
      • multipart/form-data: ファイルアップロードを含むフォームで主に使用されます。各フォームフィールドやファイルが「パート」として区切られ、それぞれが独自のヘッダー(Content-Dispositionなど)を持ちます。
  3. http.Request のフォーム関連メソッド:

    • Request.ParseForm():
      • URLのクエリパラメータと、application/x-www-form-urlencoded 形式のPOST/PUTリクエストボディを解析し、結果を Request.Form マップに格納します。
      • このメソッドは、解析中にエラーが発生する可能性があります(例: 不正なURLエンコード)。
    • Request.ParseMultipartForm(maxMemory int64):
      • multipart/form-data 形式のPOST/PUTリクエストボディを解析します。
      • アップロードされたファイルは一時的にメモリまたはディスクに保存されます。maxMemory は、メモリに保持する最大バイト数を指定します。これを超えるとディスクに書き込まれます。
      • このメソッドも、解析中にエラーが発生する可能性があります(例: 不正なマルチパート形式、maxMemory 超過)。
    • Request.Form:
      • ParseForm または ParseMultipartForm が呼び出された後に、URLクエリパラメータとPOST/PUTフォームデータの両方を含むマップ(map[string][]string)です。POST/PUTデータが優先されます。
    • Request.PostForm:
      • ParseForm または ParseMultipartForm が呼び出された後に、POST/PUTリクエストボディのフォームデータのみを含むマップ(map[string][]string)です。URLクエリパラメータは含まれません。
    • Request.FormValue(key string):
      • Request.Form から指定されたキーの最初の値を取得する便利なメソッドです。
      • 必要に応じて ParseMultipartFormParseForm を自動的に呼び出します。
    • Request.PostFormValue(key string):
      • Request.PostForm から指定されたキーの最初の値を取得する便利なメソッドです。
      • 必要に応じて ParseMultipartFormParseForm を自動的に呼び出します。URLクエリパラメータは無視されます。
  4. Go言語のエラーハンドリング:

    • Goでは、エラーは戻り値として明示的に返されます。慣例として、関数の最後の戻り値は error 型です。
    • 呼び出し元は、返された errornil でない場合にエラーが発生したと判断し、適切に処理する必要があります。
    • しかし、このコミットのように、特定の状況下ではエラーを「無視する」という設計判断がなされることもあります。これは、エラーが致命的ではない場合や、部分的な成功で十分な場合に適用されます。

技術的詳細

このコミットの技術的な核心は、FormValuePostFormValue メソッドが、内部で呼び出す ParseForm および ParseMultipartForm から返されるエラーをどのように扱うか、という点にあります。

Goの net/http パッケージでは、Request.ParseForm()Request.ParseMultipartForm() は、フォームデータの解析に失敗した場合に error を返します。例えば、以下のような場合にエラーが発生する可能性があります。

  • ParseForm() の場合:
    • URLエンコードされたフォームデータが不正な形式である。
    • リクエストボディが非常に大きく、メモリにロードできない。
  • ParseMultipartForm() の場合:
    • Content-Type ヘッダーが multipart/form-data であるにもかかわらず、ボディの形式が不正である(境界文字列が見つからない、パートのヘッダーが不正など)。
    • maxMemory で指定されたメモリ制限を超過し、かつディスクへの書き込みも失敗した場合。

このコミット以前は、FormValuePostFormValue がこれらの内部関数を呼び出した際にエラーが返された場合、そのエラーが FormValuePostFormValue の呼び出し元に伝播されることはありませんでした。しかし、そのエラーが Request.FormRequest.PostForm の状態にどのような影響を与えるか、そして FormValuePostFormValue が値を返すかどうかは、実装の詳細に依存し、ドキュメント上も不明瞭でした。

このコミットは、この挙動を明確にし、ドキュメントに明記することを目的としています。具体的には、FormValuePostFormValue は、内部的な解析関数がエラーを返したとしても、そのエラーを「無視」し、可能な限りフォームから値を抽出しようとします。これは、エラーが発生したとしても、Request.FormRequest.PostForm が部分的にでもデータを含んでいる可能性があるためです。

例えば、ParseMultipartFormmaxMemory を超えたためにエラーを返した場合でも、既に解析された部分のフォームデータは Request.Form に格納されている可能性があります。この変更により、FormValuePostFormValue は、そのような場合でも、既に解析済みのデータから値を返すことができるようになります。

この設計判断は、FormValuePostFormValue が「特定のキーの値を簡単に取得する」という目的を果たす上で、解析エラーの詳細なハンドリングはこれらの関数の責任範囲外であるという哲学に基づいています。もし開発者がフォーム解析のエラーを詳細に処理する必要がある場合は、Request.ParseForm()Request.ParseMultipartForm() を直接呼び出し、その戻り値のエラーをチェックすることが推奨されます。

この変更は、コードの挙動そのものを大きく変えるものではなく、むしろその挙動をドキュメント(コメント)で明確にすることで、開発者がこれらのメソッドをより安心して利用できるようにすることを意図しています。

コアとなるコードの変更箇所

変更は src/pkg/net/http/request.go ファイルの以下の部分です。

--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -807,7 +807,8 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error {
 
 // FormValue returns the first value for the named component of the query.
 // POST and PUT body parameters take precedence over URL query string values.
-// FormValue calls ParseMultipartForm and ParseForm if necessary.
+// FormValue calls ParseMultipartForm and ParseForm if necessary and ignores
+// any errors returned by these functions.
 // To access multiple values of the same key use ParseForm.
 func (r *Request) FormValue(key string) string {\n \tif r.Form == nil {\n@@ -821,7 +822,8 @@ func (r *Request) FormValue(key string) string {\n 
 // PostFormValue returns the first value for the named component of the POST
 // or PUT request body. URL query parameters are ignored.
-// PostFormValue calls ParseMultipartForm and ParseForm if necessary.
+// PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores
+// any errors returned by these functions.
 func (r *Request) PostFormValue(key string) string {\n \tif r.PostForm == nil {\n \t\tr.ParseMultipartForm(defaultMaxMemory)\n```

## コアとなるコードの解説

このコミットによるコードの変更は、実際のロジックの変更ではなく、`FormValue` と `PostFormValue` メソッドのドキュメンテーションコメントの更新のみです。

*   **`FormValue` メソッドのコメント変更**:
    *   変更前: `// FormValue calls ParseMultipartForm and ParseForm if necessary.`
    *   変更後: `// FormValue calls ParseMultipartForm and ParseForm if necessary and ignores\n// any errors returned by these functions.`
    *   この変更により、`FormValue` が内部で `ParseMultipartForm` や `ParseForm` を呼び出す際に、これらの関数がエラーを返しても、そのエラーが `FormValue` の呼び出し元に伝播せず、`FormValue` 自身がそのエラーを無視するという挙動が明確に記述されました。

*   **`PostFormValue` メソッドのコメント変更**:
    *   変更前: `// PostFormValue calls ParseMultipartForm and ParseForm if necessary.`
    *   変更後: `// PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores\n// any errors returned by these functions.`
    *   `FormValue` と同様に、`PostFormValue` も内部的なフォーム解析エラーを無視するという挙動が明記されました。

これらのコメントの追加は、`FormValue` と `PostFormValue` の設計意図と実際の挙動をより正確に反映させるためのものです。これにより、開発者はこれらのメソッドを使用する際に、フォーム解析エラーのハンドリングについて心配する必要がないことを明確に理解できます。もしエラーの詳細な情報が必要な場合は、`ParseForm` や `ParseMultipartForm` を直接呼び出すべきであるというメッセージも暗に伝えています。

## 関連リンク

*   Go CL (Change List) 102640046: [https://golang.org/cl/102640046](https://golang.org/cl/102640046)

## 参考にした情報源リンク

*   Go言語 `net/http` パッケージ公式ドキュメント: [https://pkg.go.dev/net/http](https://pkg.go.dev/net/http)
*   Go言語におけるエラーハンドリングの慣習: [https://go.dev/blog/error-handling-and-go](https://go.dev/blog/error-handling-and-go)
*   HTTP `Content-Type` ヘッダー (MDN Web Docs): [https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type)
*   `application/x-www-form-urlencoded` と `multipart/form-data` の違い: [https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/POST](https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/POST) (関連セクションを参照)