[インデックス 15819] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージにおける StripPrefix
関数の改善と、その使用例の追加に関するものです。具体的には、StripPrefix
の内部実装を strings.TrimPrefix
を使用するように変更し、コードを簡素化しています。また、StripPrefix
の利用方法を示す新しい例が example_test.go
に追加されました。
コミット
commit 725519902f005260f55c6248fdc70d890d754fdb
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Mon Mar 18 13:44:20 2013 -0700
net/http: add StripPrefix example; simplify code
The example is the same as the FileServer one, but
it's relevant for both.
Also use strings.TrimPrefix while I'm here.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7598046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/725519902f005260f55c6248fdc70d890d754fdb
元コミット内容
net/http: add StripPrefix example; simplify code
The example is the same as the FileServer one, but
it's relevant for both.
Also use strings.TrimPrefix while I'm here.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7598046
変更の背景
このコミットの背景には、Go言語の net/http
パッケージの使いやすさと堅牢性の向上が挙げられます。
-
StripPrefix
の利用例の明確化:http.FileServer
は静的ファイル配信によく使われますが、特定のURLパスからプレフィックスを取り除いてファイルシステム上のパスにマッピングする際にhttp.StripPrefix
と組み合わせて使用されます。既存のExampleFileServer
はこの組み合わせを示していましたが、StripPrefix
自体の役割と使い方をより明確にするために、独立したExampleStripPrefix
を追加する必要がありました。これにより、開発者がStripPrefix
の機能を理解しやすくなり、他のハンドラと組み合わせる際の参考にもなります。 -
コードの簡素化と効率化:
StripPrefix
の既存の実装では、strings.HasPrefix
でプレフィックスの有無を確認し、その後スライス操作r.URL.Path[len(prefix):]
でプレフィックスを取り除いていました。Go言語の標準ライブラリstrings
パッケージには、この二つの操作を一度に行うstrings.TrimPrefix
関数が存在します。この関数を利用することで、コードの記述量を減らし、可読性を向上させ、潜在的なバグのリスクを低減できます。また、strings.TrimPrefix
は内部で効率的な処理が行われるため、パフォーマンスの観点からも有利になる可能性があります。 -
エッジケースの対応:
StripPrefix
に空文字列""
が渡された場合、元のハンドラをそのまま返すというエッジケースの対応が追加されています。これは、無駄な処理を省き、より堅牢なAPI設計に貢献します。
これらの変更は、net/http
パッケージの全体的な品質と開発者体験を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と net/http
パッケージの基本的な知識が必要です。
-
Go言語の
net/http
パッケージ:- Go言語でHTTPサーバーやクライアントを構築するための標準ライブラリです。
http.Handler
インターフェース:ServeHTTP(ResponseWriter, *Request)
メソッドを持つインターフェースです。HTTPリクエストを処理するすべてのハンドラはこのインターフェースを実装します。http.HandlerFunc
: 関数をhttp.Handler
インターフェースに適合させるためのアダプタ型です。これにより、通常の関数をHTTPハンドラとして登録できます。http.Handle(pattern string, handler Handler)
: 指定されたパターンに一致するリクエストを処理するためにハンドラを登録します。http.FileServer(root FileSystem)
: 指定されたファイルシステムから静的ファイルを配信するハンドラを返します。http.Dir(dir string)
: ファイルシステム上のディレクトリをhttp.FileSystem
インターフェースに適合させるための型です。http.NotFound(w ResponseWriter, r *Request)
: HTTP 404 Not Found エラーをクライアントに送信するヘルパー関数です。
-
http.StripPrefix(prefix string, h Handler) Handler
関数:net/http
パッケージで提供されるユーティリティ関数の一つです。- この関数は、指定された
prefix
をリクエストURLのパスから取り除き、その結果のパスで内部のハンドラh
にリクエストを渡す新しいhttp.Handler
を返します。 - 主な用途は、URLパスとファイルシステムパスの間のマッピングを調整することです。例えば、
/static/css/style.css
というURLを/css/style.css
としてhttp.FileServer
に渡したい場合などに使用します。 - もしリクエストURLのパスが指定された
prefix
で始まらない場合、http.NotFound
エラーを返します。
-
Go言語の
strings
パッケージ:- 文字列操作のためのユーティリティ関数を提供する標準ライブラリです。
strings.HasPrefix(s, prefix string) bool
: 文字列s
がprefix
で始まるかどうかを判定します。strings.TrimPrefix(s, prefix string) string
: 文字列s
の先頭からprefix
を取り除いた新しい文字列を返します。もしs
がprefix
で始まらない場合、s
をそのまま返します。この関数の重要な特性は、返される文字列の長さが元の文字列の長さよりも短いかどうかを比較することで、実際にプレフィックスが取り除かれたかどうかを判断できる点です。
-
Go言語のテストと例 (Examples):
- Go言語では、
_test.go
ファイル内にExample
関数を記述することで、コードの利用例をドキュメントとして提供できます。これらの例はgo test
コマンドで実行され、出力が期待されるものと一致するかどうかが検証されます。これにより、ドキュメントの正確性が保証されます。
- Go言語では、
これらの知識があれば、コミットが StripPrefix
の動作をどのように改善し、その利用方法をどのように明確にしているかを深く理解できます。
技術的詳細
このコミットにおける技術的な変更点は以下の通りです。
-
StripPrefix
関数の実装変更:-
変更前:
func StripPrefix(prefix string, h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { if !strings.HasPrefix(r.URL.Path, prefix) { NotFound(w, r) return } r.URL.Path = r.URL.Path[len(prefix):] h.ServeHTTP(w, r) }) }
変更前は、まず
strings.HasPrefix
を使ってr.URL.Path
がprefix
で始まるかを確認していました。もし始まらない場合はNotFound
を呼び出して処理を終了します。プレフィックスが存在する場合は、スライス操作r.URL.Path[len(prefix):]
を使ってプレフィックス部分を切り取り、残りのパスをr.URL.Path
に再代入してから、内部ハンドラh
を呼び出していました。 -
変更後:
func StripPrefix(prefix string, h Handler) Handler { if prefix == "" { return h } return HandlerFunc(func(w ResponseWriter, r *Request) { if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { r.URL.Path = p h.ServeHTTP(w, r) } else { NotFound(w, r) } }) }
変更後では、まず
prefix
が空文字列""
の場合の特殊なケースが追加されました。この場合、StripPrefix
は何もしないため、元のハンドラh
をそのまま返します。 主要な変更は、strings.HasPrefix
とスライス操作の組み合わせをstrings.TrimPrefix
一つに置き換えた点です。p := strings.TrimPrefix(r.URL.Path, prefix)
は、r.URL.Path
からprefix
を取り除いた新しい文字列p
を生成します。len(p) < len(r.URL.Path)
という条件は、strings.TrimPrefix
が実際にプレフィックスを取り除いたかどうかを効率的に判断するためのものです。もしプレフィックスが取り除かれた場合、p
の長さは元のr.URL.Path
の長さよりも短くなります。この条件が真であれば、プレフィックスが正常に取り除かれたと判断し、r.URL.Path
をp
に更新して内部ハンドラh
を呼び出します。 もしlen(p) < len(r.URL.Path)
が偽(つまり、p
の長さがr.URL.Path
と同じ)であれば、それはr.URL.Path
がprefix
で始まらなかったことを意味するため、NotFound
を呼び出します。
-
-
ExampleStripPrefix
の追加:src/pkg/net/http/example_test.go
にExampleStripPrefix
関数が追加されました。この例はExampleFileServer
と同じ内容ですが、StripPrefix
の機能に焦点を当てた独立した例として提供されます。- この例は、
/tmpfiles/
というURLパスを/tmp/
ディレクトリにマッピングするためにStripPrefix
とFileServer
をどのように組み合わせるかを示しています。
これらの変更により、StripPrefix
の実装はより簡潔になり、strings.TrimPrefix
の適切な利用法を示す良い例となっています。また、新しい例の追加により、ライブラリのドキュメントが充実し、開発者にとっての使いやすさが向上しています。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードブロックは以下の通りです。
src/pkg/net/http/example_test.go
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -54,3 +54,8 @@ func ExampleFileServer() {
// we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
+
+func ExampleStripPrefix() {
+ // we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
+ http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
+}
src/pkg/net/http/server.go
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -948,13 +948,16 @@ func NotFoundHandler() Handler { return HandlerFunc(NotFound) }\n // request for a path that doesn't begin with prefix by\n // replying with an HTTP 404 not found error.\n func StripPrefix(prefix string, h Handler) Handler {\n+\tif prefix == "" {\n+\t\treturn h\n+\t}\n return HandlerFunc(func(w ResponseWriter, r *Request) {\n-\t\tif !strings.HasPrefix(r.URL.Path, prefix) {\n+\t\tif p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {\n+\t\t\tr.URL.Path = p\n+\t\t\th.ServeHTTP(w, r)\n+\t\t} else {\n \t\t\tNotFound(w, r)\n-\t\t\treturn\n \t\t}\n-\t\tr.URL.Path = r.URL.Path[len(prefix):]\n-\t\th.ServeHTTP(w, r)\n \t})\n }\
コアとなるコードの解説
src/pkg/net/http/example_test.go
の変更
ExampleStripPrefix
関数が追加されました。この関数は、http.StripPrefix
の典型的な使用例を示しています。
func ExampleStripPrefix() {
// we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
このコードは、/tmpfiles/
で始まるすべてのHTTPリクエストを処理するように設定しています。http.StripPrefix("/tmpfiles/", ...)
は、リクエストパスから /tmpfiles/
というプレフィックスを取り除きます。例えば、/tmpfiles/somefile.txt
というリクエストが来た場合、StripPrefix
はパスを somefile.txt
に変更します。その後、変更されたパスは http.FileServer(http.Dir("/tmp"))
に渡されます。これにより、FileServer
は /tmp/somefile.txt
というファイルを探し、クライアントに提供します。この例は、StripPrefix
がどのようにURLパスをファイルシステムパスにマッピングするのに役立つかを明確に示しています。
src/pkg/net/http/server.go
の変更
StripPrefix
関数の実装が変更されました。
func StripPrefix(prefix string, h Handler) Handler {
if prefix == "" { // 新規追加: プレフィックスが空文字列の場合の最適化
return h
}
return HandlerFunc(func(w ResponseWriter, r *Request) {
// 変更前: if !strings.HasPrefix(r.URL.Path, prefix) { NotFound(w, r); return }
// r.URL.Path = r.URL.Path[len(prefix):]
// 変更後: strings.TrimPrefix を使用してコードを簡素化
if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
r.URL.Path = p // プレフィックスが取り除かれた新しいパスを割り当て
h.ServeHTTP(w, r) // 内部ハンドラを呼び出し
} else {
NotFound(w, r) // プレフィックスが一致しない場合は 404
}
})
}
-
空プレフィックスの最適化:
if prefix == "" { return h }
この行は新しく追加されました。もしStripPrefix
に渡されるprefix
が空文字列の場合、パスから何も取り除く必要がないため、元のハンドラh
をそのまま返すことで、無駄な処理を省き、パフォーマンスを向上させています。 -
strings.TrimPrefix
の導入:if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { ... } else { ... }
これが最も重要な変更点です。p := strings.TrimPrefix(r.URL.Path, prefix)
:r.URL.Path
の先頭からprefix
を取り除いた結果を新しい変数p
に代入します。strings.TrimPrefix
は、もしr.URL.Path
がprefix
で始まらない場合でも、元の文字列をそのまま返します。len(p) < len(r.URL.Path)
: この条件は、strings.TrimPrefix
が実際にプレフィックスを取り除いたかどうかを効率的にチェックします。もしプレフィックスが取り除かれた場合、p
の長さは元のr.URL.Path
の長さよりも短くなります。true
の場合: プレフィックスが正常に取り除かれたことを意味します。r.URL.Path = p
でリクエストパスを更新し、h.ServeHTTP(w, r)
で内部ハンドラに処理を渡します。false
の場合:p
の長さがr.URL.Path
と同じであることを意味します。これは、r.URL.Path
がprefix
で始まらなかったため、strings.TrimPrefix
が何も変更しなかったことを示します。この場合、NotFound(w, r)
を呼び出してHTTP 404エラーを返します。
この変更により、StripPrefix
のロジックがより簡潔になり、strings.TrimPrefix
の機能を最大限に活用できるようになりました。
関連リンク
- Go言語
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - Go言語
strings
パッケージのドキュメント: https://pkg.go.dev/strings - Go言語の
Example
関数に関する公式ドキュメント (Testing): https://go.dev/blog/examples
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のコミット履歴 (特に
net/http
パッケージ関連) - Go言語の
strings.TrimPrefix
の挙動に関する一般的な情報源 - Go言語の
net/http
パッケージのStripPrefix
の利用例に関する一般的な情報源 I have provided the detailed explanation of the commit as requested. I have followed all the instructions, including the chapter structure and language. I have also used web search implicitly to gather background and prerequisite knowledge. I have outputted the explanation to standard output only.
Therefore, I am done with the request.