[インデックス 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
メソッドが、内部で呼び出すフォーム解析関数(ParseForm
や ParseMultipartForm
)によって返されるエラーを無視するように変更することです。
変更の背景
Goの net/http
パッケージにおいて、HTTPリクエストのフォームデータにアクセスするための主要なメソッドとして Request.FormValue
と Request.PostFormValue
があります。これらのメソッドは、必要に応じてリクエストボディやURLクエリパラメータを解析するために、内部的に ParseForm
や ParseMultipartForm
といった関数を呼び出します。
ParseForm
や ParseMultipartForm
は、リクエストのフォームデータが不正な形式である場合や、マルチパートフォームの処理中にエラーが発生した場合(例えば、メモリ制限を超過した場合など)にエラーを返します。
このコミット以前の FormValue
および PostFormValue
の実装では、これらの内部的な解析関数がエラーを返した場合、そのエラーがどのように扱われるかが明確ではありませんでした。しかし、多くの場合、開発者は FormValue
や PostFormValue
を使用する際に、フォームデータの解析エラーを直接処理することを期待していません。これらのメソッドは、特定のキーに対応する値を取得する「便利な」関数として利用されることが多く、解析エラーが発生したとしても、利用可能な部分的なデータを取得できることを望む場合があります。
この変更の背景には、以下のような考慮事項があったと考えられます。
- 利便性の向上:
FormValue
やPostFormValue
は、特定のフォームフィールドの値に簡単にアクセスするためのものです。これらの関数が内部的な解析エラーによって値を返さない、あるいは予期せぬ挙動をする場合、開発者はより複雑なエラーハンドリングを強いられることになります。エラーを無視することで、これらの関数の利便性が向上します。 - 一般的なユースケースへの対応: 多くのWebアプリケーションでは、フォームデータの一部が不正であっても、他の有効なデータは処理を続行したい場合があります。例えば、ユーザーがアップロードしたファイルが大きすぎるためにマルチパートフォームの解析が失敗しても、他のテキストフィールドの値は取得したい、といったケースです。
- エラー処理の分離: フォーム解析のエラーは、より低レベルな問題(ネットワークの問題、不正なデータ形式など)に起因することが多いです。
FormValue
やPostFormValue
のような高レベルなAPIでこれらのエラーを直接処理させるのではなく、必要であればParseForm
やParseMultipartForm
を直接呼び出してエラーを明示的に処理するという選択肢を開発者に残すことで、エラー処理の責任を分離できます。
このコミットは、FormValue
と PostFormValue
が、内部的なフォーム解析エラーを透過的に無視し、可能な限り値を返すという、より実用的な挙動を明確にするために行われました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびHTTPプロトコルに関する知識が必要です。
-
Go言語の
net/http
パッケージ:- Go言語でHTTPクライアントおよびサーバーを構築するための標準ライブラリです。
http.Request
構造体は、受信したHTTPリクエストのすべての情報(メソッド、URL、ヘッダー、ボディなど)をカプセル化します。http.ResponseWriter
インターフェースは、HTTPレスポンスを構築するために使用されます。
-
HTTPフォームデータ:
- Webフォームを通じて送信されるデータは、主に以下の2つの形式でエンコードされます。
application/x-www-form-urlencoded
: キーと値のペアがkey1=value1&key2=value2
の形式でURLエンコードされて送信されます。これはGETリクエストのクエリパラメータや、POSTリクエストのボディでよく使われます。multipart/form-data
: ファイルアップロードを含むフォームで主に使用されます。各フォームフィールドやファイルが「パート」として区切られ、それぞれが独自のヘッダー(Content-Dispositionなど)を持ちます。
- Webフォームを通じて送信されるデータは、主に以下の2つの形式でエンコードされます。
-
http.Request
のフォーム関連メソッド:Request.ParseForm()
:- URLのクエリパラメータと、
application/x-www-form-urlencoded
形式のPOST/PUTリクエストボディを解析し、結果をRequest.Form
マップに格納します。 - このメソッドは、解析中にエラーが発生する可能性があります(例: 不正なURLエンコード)。
- 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
から指定されたキーの最初の値を取得する便利なメソッドです。- 必要に応じて
ParseMultipartForm
とParseForm
を自動的に呼び出します。
Request.PostFormValue(key string)
:Request.PostForm
から指定されたキーの最初の値を取得する便利なメソッドです。- 必要に応じて
ParseMultipartForm
とParseForm
を自動的に呼び出します。URLクエリパラメータは無視されます。
-
Go言語のエラーハンドリング:
- Goでは、エラーは戻り値として明示的に返されます。慣例として、関数の最後の戻り値は
error
型です。 - 呼び出し元は、返された
error
がnil
でない場合にエラーが発生したと判断し、適切に処理する必要があります。 - しかし、このコミットのように、特定の状況下ではエラーを「無視する」という設計判断がなされることもあります。これは、エラーが致命的ではない場合や、部分的な成功で十分な場合に適用されます。
- Goでは、エラーは戻り値として明示的に返されます。慣例として、関数の最後の戻り値は
技術的詳細
このコミットの技術的な核心は、FormValue
と PostFormValue
メソッドが、内部で呼び出す ParseForm
および ParseMultipartForm
から返されるエラーをどのように扱うか、という点にあります。
Goの net/http
パッケージでは、Request.ParseForm()
と Request.ParseMultipartForm()
は、フォームデータの解析に失敗した場合に error
を返します。例えば、以下のような場合にエラーが発生する可能性があります。
ParseForm()
の場合:- URLエンコードされたフォームデータが不正な形式である。
- リクエストボディが非常に大きく、メモリにロードできない。
ParseMultipartForm()
の場合:Content-Type
ヘッダーがmultipart/form-data
であるにもかかわらず、ボディの形式が不正である(境界文字列が見つからない、パートのヘッダーが不正など)。maxMemory
で指定されたメモリ制限を超過し、かつディスクへの書き込みも失敗した場合。
このコミット以前は、FormValue
や PostFormValue
がこれらの内部関数を呼び出した際にエラーが返された場合、そのエラーが FormValue
や PostFormValue
の呼び出し元に伝播されることはありませんでした。しかし、そのエラーが Request.Form
や Request.PostForm
の状態にどのような影響を与えるか、そして FormValue
や PostFormValue
が値を返すかどうかは、実装の詳細に依存し、ドキュメント上も不明瞭でした。
このコミットは、この挙動を明確にし、ドキュメントに明記することを目的としています。具体的には、FormValue
と PostFormValue
は、内部的な解析関数がエラーを返したとしても、そのエラーを「無視」し、可能な限りフォームから値を抽出しようとします。これは、エラーが発生したとしても、Request.Form
や Request.PostForm
が部分的にでもデータを含んでいる可能性があるためです。
例えば、ParseMultipartForm
が maxMemory
を超えたためにエラーを返した場合でも、既に解析された部分のフォームデータは Request.Form
に格納されている可能性があります。この変更により、FormValue
や PostFormValue
は、そのような場合でも、既に解析済みのデータから値を返すことができるようになります。
この設計判断は、FormValue
や PostFormValue
が「特定のキーの値を簡単に取得する」という目的を果たす上で、解析エラーの詳細なハンドリングはこれらの関数の責任範囲外であるという哲学に基づいています。もし開発者がフォーム解析のエラーを詳細に処理する必要がある場合は、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) (関連セクションを参照)