[インデックス 14124] ファイルの概要
このコミットは、Go言語の公式ドキュメントの一部である「Go Wiki」チュートリアル (doc/articles/wiki/index.html
) に対する多数の修正と改善を目的としています。具体的には、既存のコンテンツの構文更新、表現の改善、そして特にエラーハンドリングに焦点を当てた新しいサンプルプログラム (part3.go
および part3-errorhandling.go
) の追加が行われました。これにより、チュートリアルの正確性、明瞭性、および堅牢性が向上しています。
コミット
このコミットは、Go Wikiチュートリアル記事のコンテンツを更新し、より堅牢なエラーハンドリングとコード構造を示すための新しいGoプログラム例を追加しました。
- コミットハッシュ:
dad1228cc378f5860a111201ed24ba88cf992a73
- 作者: Jimmy Zelinskie jimmyzelinskie@gmail.com
- 日付: 2012年10月11日 木曜日 13:07:34 +1100
- コミットメッセージ:
doc/articles/wiki: numerous fixes Fixes #3733 Fixes #2149 Updated Syntax Added part3.go example program Added part3-errorhandling.go example program Improved wording in some places R=golang-dev, adg, minux.ma CC=golang-dev https://golang.org/cl/6636048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dad1228cc378f5860a111201ed24ba88cf992a73
元コミット内容
doc/articles/wiki: numerous fixes
Fixes #3733
Fixes #2149
Updated Syntax
Added part3.go example program
Added part3-errorhandling.go example program
Improved wording in some places
R=golang-dev, adg, minux.ma
CC=golang-dev
https://golang.org/cl/6636048
変更の背景
このコミットの主な背景には、Go Wikiチュートリアルの既存の課題と改善の必要性がありました。コミットメッセージに記載されている #3733
と #2149
は、このチュートリアルに関連する既存のバグや改善要求を示唆しています。
具体的な背景としては、以下の点が挙げられます。
- チュートリアルの正確性と明瞭性の向上: チュートリアル内のコードスニペットや説明が、Go言語の進化やベストプラクティスに合わせて古くなっていた可能性があります。特に、エラーハンドリングの重要性やその適切な実装方法について、より詳細かつ正確なガイダンスが求められていました。
- エラーハンドリングの強化: 従来のチュートリアルでは、エラーが無視されるケースが多く、実際のアプリケーション開発では推奨されない「悪い習慣」として指摘される可能性がありました。このコミットは、エラーを適切に処理し、ユーザーにフィードバックを返す方法を具体的に示すことで、より堅牢なアプリケーション開発の指針を提供しようとしています。
- 段階的な学習パスの提供:
part3.go
とpart3-errorhandling.go
という2つの新しいサンプルプログラムを追加することで、読者がエラーハンドリングの概念を段階的に理解できるように意図されています。part3.go
は基本的な機能拡張を示し、part3-errorhandling.go
はそれにエラーハンドリングのロジックを組み込んだ、より実践的な例として機能します。 - 構文と表現の統一: チュートリアル全体で使われているGoの構文や一般的な表現を最新の状態に保ち、読者が混乱しないようにするための修正も含まれています。
これらの変更は、Go WikiチュートリアルがGo言語のWebアプリケーション開発の入門として、より高品質で実践的なリソースとなることを目指しています。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語およびWeb開発に関する基本的な知識が必要です。
-
Go言語の基本:
- パッケージとインポート:
fmt
,io/ioutil
,net/http
,html/template
,regexp
,errors
などの標準ライブラリの役割とインポート方法。 - 構造体 (Struct): データの集合を定義するための型。
Page
構造体のように、関連するデータをまとめるために使用されます。 - メソッド: 構造体に関連付けられた関数。レシーバ (例:
(p *Page)
) を持つことで、その構造体のインスタンスに対して操作を実行できます。 - 関数: Goプログラムの基本的な実行単位。複数の戻り値を返すことができるのが特徴です。
- スライス (
[]byte
): 可変長シーケンス。特にファイルの内容やHTTPリクエストのボディなど、バイト列を扱う際によく使用されます。 - ポインタ: 変数のメモリアドレスを指す型。Goでは、大きな構造体を関数間で渡す際にコピーを避けるためや、メソッドがレシーバの値を変更できるようにするために使用されます。
- エラーハンドリング: Go言語におけるエラーは、関数の最後の戻り値として
error
型で返されるのが一般的です。nil
はエラーがないことを意味します。if err != nil
のパターンでエラーをチェックし、適切に処理することが推奨されます。 - ブランク識別子 (
_
): 戻り値のうち、不要なものを破棄するために使用されます。ただし、エラー値を無視することは、本番環境のコードでは一般的に「悪い習慣」と見なされます。 - 関数リテラルとクロージャ: 関数を値として扱い、変数に代入したり、他の関数の引数として渡したり、戻り値として返したりできます。クロージャは、それが定義されたスコープの外部変数を参照できる関数リテラルです。これにより、共通のロジック(例: エラーチェック)を複数のハンドラで再利用するパターンを実装できます。
- パッケージとインポート:
-
Web開発の基本:
- HTTPプロトコル: Webブラウザとサーバー間の通信規約。リクエスト (Request) とレスポンス (Response) の概念。
- HTTPハンドラ: HTTPリクエストを処理し、HTTPレスポンスを生成する関数。Goの
net/http
パッケージでは、http.Handler
インターフェースまたはhttp.HandlerFunc
型がこれに該当します。 - ルーティング: 特定のURLパスを対応するハンドラにマッピングするプロセス。
http.HandleFunc
がこれを行います。 - HTTPステータスコード: HTTPレスポンスの一部として返される3桁の数字で、リクエストの結果を示します。
http.StatusFound
(302 Found): リソースが一時的に別のURIに移動したことを示し、リダイレクトに使用されます。http.StatusInternalServerError
(500 Internal Server Error): サーバー側で予期せぬエラーが発生したことを示します。http.StatusNotFound
(404 Not Found): リクエストされたリソースが見つからないことを示します。
- HTMLテンプレート: 動的なWebページを生成するための仕組み。Goの
html/template
パッケージは、Goのデータ構造をHTMLに埋め込む機能を提供し、XSS (Cross-Site Scripting) 攻撃からの保護も行います。 - フォーム処理: Webフォームからのデータ (
r.FormValue
) を受け取り、処理する方法。
-
ファイルシステム操作:
- ファイル読み書き:
io/ioutil.ReadFile
やio/ioutil.WriteFile
を使ったファイルの読み書き。 - ファイルパーミッション:
0600
のような八進数表記でファイルの読み書き権限を設定する方法(Unix系システムの場合)。
- ファイル読み書き:
-
正規表現:
regexp
パッケージを使った正規表現のコンパイルとマッチング。特にregexp.MustCompile
は、正規表現のコンパイルに失敗した場合にパニックを起こす関数です。
これらの知識があれば、コミットで行われた変更の意図と実装の詳細を深く理解することができます。
技術的詳細
このコミットは、Go Wikiチュートリアルの複数の側面を改善しています。
-
index.html
のコンテンツ更新:- 構文と表現の修正: チュートリアル全体で、より正確で自然な表現に修正されています。例えば、「octal integer constant」が「octal integer literal」に変更されるなど、細かな用語の修正が行われています。
loadPage
関数の説明の明確化:loadPage
関数が複数の戻り値(*Page
とerror
)を返すこと、およびerror
値がnil
の場合に成功を示すことが強調されています。- ブランク識別子 (
_
) の使用に関する注意喚起: エラー値を_
で無視することが「悪い習慣」であるという警告が追加され、後で適切に処理されることが示唆されています。 fmt
パッケージの削除:html/template
パッケージの使用によりfmt.Fprintf
が不要になったため、fmt
パッケージのインポートが削除されることが明記されています。- エラーハンドリングの導入と説明の拡張:
- 存在しないページへのアクセスに対する挙動がより正確に記述され、エラーを無視した場合の「HTMLを含むページが表示される」という挙動が説明されています。
viewHandler
におけるloadPage
のエラーハンドリングが導入され、ページが見つからない場合に/edit/
ページへリダイレクトするhttp.Redirect
の使用が示されています。renderTemplate
関数におけるテンプレートのパースエラーのハンドリングが追加され、http.Error
を使用して「Internal Server Error」を返す方法が示されています。saveHandler
におけるp.save()
のエラーハンドリングが追加され、エラー発生時にhttp.Error
でエラーメッセージを返す方法が示されています。
- テンプレートキャッシュの導入:
renderTemplate
が毎回ParseFiles
を呼び出す非効率性を指摘し、プログラム初期化時に一度だけテンプレートをパースしてキャッシュするtemplate.Must
の使用が推奨されています。 - タイトル検証の導入: 正規表現 (
regexp
パッケージ) を使用してページタイトルを検証するgetTitle
関数が導入され、無効なタイトルに対して「404 Not Found」エラーを返す方法が示されています。 - ハンドララッパー (クロージャ) の導入: 共通の検証ロジック(タイトル検証など)を複数のハンドラで再利用するために、
makeHandler
という関数リテラル(クロージャ)を使用してハンドラをラップする高度なパターンが導入されています。これにより、コードの重複が削減され、保守性が向上します。
-
新しいGoサンプルプログラムの追加:
doc/articles/wiki/part3.go
:- これは、
html/template
を使用した基本的なWikiアプリケーションの例です。 Page
構造体、save
メソッド、loadPage
関数が含まれます。renderTemplate
関数が導入され、テンプレートのレンダリングを抽象化しています。viewHandler
とeditHandler
が実装されていますが、viewHandler
ではloadPage
のエラーが_
で無視されており、saveHandler
はコメントアウトされています。これは、エラーハンドリングがまだ完全ではない、または次のステップで導入されることを示す中間的な状態を示しています。
- これは、
doc/articles/wiki/part3-errorhandling.go
:part3.go
をベースに、より堅牢なエラーハンドリングが組み込まれたバージョンです。viewHandler
では、loadPage
がエラーを返した場合にhttp.Redirect
を使用して/edit/
ページにリダイレクトします。saveHandler
では、p.save()
がエラーを返した場合にhttp.Error
を使用して「Internal Server Error」を返します。main
関数でsaveHandler
も登録されており、完全なWikiアプリケーションの機能が提供されています。- このファイルは、Go言語における実践的なエラーハンドリングのベストプラクティスを示すことを目的としています。
これらの変更は、Go Wikiチュートリアルをより包括的で、実践的なWebアプリケーション開発のガイドとして進化させています。特に、エラーハンドリングの重要性と、それをGo言語でどのように実装するかという点に重点が置かれています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に doc/articles/wiki/index.html
のコンテンツ更新と、新しく追加された2つのGoプログラムファイルにあります。
-
doc/articles/wiki/index.html
:loadPage
の説明変更:loadPage
が*Page
とerror
の2つの値を返すこと、およびエラー処理の重要性に関する記述が追加・修正されました。--- a/doc/articles/wiki/index.html +++ b/doc/articles/wiki/index.html @@ -110,35 +110,37 @@ that is the return type of `WriteFile` (a standard library function that writes a byte slice to a file). The `save` method returns the error value, to let the application handle it should anything go wrong while writing the file. If all goes well, `Page.save()` will return -`nil` (the zero-value for pointers, interfaces, and some other +`nil` (the zero-value for pointers, interfaces, and some other types). </p> <p> -The octal integer constant `0600`, passed as the third parameter to +The octal integer literal `0600`, passed as the third parameter to `WriteFile`, indicates that the file should be created with read-write permissions for the current user only. (See the Unix man page `open(2)` for details.) </p> <p> -We will want to load pages, too: +In addition to saving pages, we will want to load pages, too: </p> {{code "doc/articles/wiki/part1-noerror.go" `/^func loadPage/` `/^}/`}} <p> The function `loadPage` constructs the file name from -`Title`, reads the file's contents into a new -`Page`, and returns a pointer to that new `page`. +the title parameter, reads the file's contents into a new +variable `body`, and returns two values: a pointer to a +`Page` literal constructed with the proper title and body +values and `nil` for the error value. </p> <p> -Functions can return multiple values. The standard library function -`io.ReadFile` returns `[]byte` and `error`. +Functions can return multiple values. The standard library function +`io.ReadFile` returns `[]byte` and `error`. In `loadPage`, error isn't being handled yet; the "blank identifier" represented by the underscore (`_`) symbol is used to throw away the -error return value (in essence, assigning the value to nothing). +error return value (in essence, assigning the value to nothing). </p>
- エラーハンドリングの導入と説明:
viewHandler
、saveHandler
、renderTemplate
におけるエラー処理のコードスニペットと説明が追加されました。特に、存在しないページへのリダイレクトや、サーバーエラーの報告方法が示されています。--- a/doc/articles/wiki/index.html +++ b/doc/articles/wiki/index.html @@ -428,28 +431,31 @@ handlers. Let's remove this duplication by moving the templating code to its own function: </p> +{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}} {{code "doc/articles/wiki/final-template.go" `/^func viewHandler/` `/^}/`}} {{code "doc/articles/wiki/final-template.go" `/^func editHandler/` `/^}/`}} -{{code "doc/articles/wiki/final-template.go" `/^func renderTemplate/` `/^}/`}} <p> -The handlers are now shorter and simpler. +If we comment out the registration of our unimplemented save handler in +`main`, we can once again build and test our program. +<a href="part3.go">Click here to view the code we've written so far.</a> </p> <h2>Handling non-existent pages</h2> <p> What if you visit <a href="http://localhost:8080/view/APageThatDoesntExist"> -`view/APageThatDoesntExist`</a>? The program will crash. This is -because it ignores the error return value from `loadPage`. Instead, -if the requested Page doesn't exist, it should redirect the client to the edit -Page so the content may be created: +`view/APageThatDoesntExist`</a>? You'll see a page containing +HTML. This is because it ignores the error return value from +`loadPage` and continues to try and fill out the template +with no data. Instead, if the requested Page doesn't exist, it should +redirect the client to the edit Page so the content may be created: </p> -{{code "doc/articles/wiki/final-noclosure.go" `/^func viewHandler/` `/^}/`}} +{{code "doc/articles/wiki/part3-errorhandling.go" `/^func viewHandler/` `/^}/`}} <p> -The `http.Redirect` function adds an HTTP status code of +The `http.Redirect` function adds an HTTP status code of `http.StatusFound` (302) and a `Location` header to the HTTP response. </p>
- テンプレートキャッシュとタイトル検証、ハンドララッパーの導入: これらの高度なトピックに関する説明と、関連するコードスニペットへの参照が追加されました。
-
doc/articles/wiki/part3.go
(新規追加):html/template
を使用した基本的なWikiアプリケーションのコード。viewHandler
ではloadPage
のエラーを無視 (_
) している。saveHandler
はmain
関数でコメントアウトされている。
// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "html/template" "io/ioutil" "net/http" ) type Page struct { Title string Body []byte } func (p *Page) save() error { filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600) } func loadPage(title string) (*Page, error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFiles(tmpl + ".html") t.Execute(w, p) } func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) // エラーを無視 renderTemplate(w, "view", p) } func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } renderTemplate(w, "edit", p) } func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) //http.HandleFunc("/save/", saveHandler) // コメントアウト http.ListenAndServe(":8080", nil) }
-
doc/articles/wiki/part3-errorhandling.go
(新規追加):part3.go
をベースに、より堅牢なエラーハンドリングが実装されたコード。viewHandler
でloadPage
のエラーをチェックし、ページが存在しない場合は/edit/
にリダイレクト。saveHandler
でp.save()
のエラーをチェックし、エラーが発生した場合はhttp.Error
でサーバーエラーを返す。main
関数でsaveHandler
も登録されている。
// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "html/template" "io/ioutil" "net/http" ) type Page struct { Title string Body []byte } func (p *Page) save() error { filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600) } func loadPage(title string) (*Page, error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFiles(tmpl + ".html") t.Execute(w, p) } func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { http.Redirect(w, r, "/edit/"+title, http.StatusFound) // ページが存在しない場合リダイレクト return } renderTemplate(w, "view", p) } func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] body := r.FormValue("body") p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) // 保存エラーを報告 return } http.Redirect(w, r, "/view/"+title, http.StatusFound) } func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) // saveHandlerを有効化 http.ListenAndServe(":8080", nil) }
これらの変更は、Go Wikiチュートリアルの内容を更新し、特にエラーハンドリングのベストプラクティスを段階的に導入することで、読者がより堅牢なWebアプリケーションを構築できるよう指導することを目的としています。
コアとなるコードの解説
このコミットの核となるのは、Go Wikiチュートリアルにおけるエラーハンドリングの導入と、それに関連するコード構造の改善です。特に part3-errorhandling.go
は、その集大成として機能します。
1. loadPage
のエラー処理とリダイレクト (viewHandler
)
viewHandler
は、ユーザーが特定のWikiページを閲覧しようとしたときに呼び出されます。このコミットでは、loadPage
関数がページを読み込む際にエラーが発生した場合(例: ファイルが存在しない場合)の処理が強化されました。
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, err := loadPage(title) // loadPageはエラーも返す
if err != nil {
// ページが存在しない場合、編集ページにリダイレクト
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return // リダイレクト後、これ以上処理を行わない
}
renderTemplate(w, "view", p)
}
loadPage(title)
は、*Page
とerror
の2つの値を返します。if err != nil
でエラーの有無をチェックします。- エラーがある場合、
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
を使用して、ユーザーをそのページの編集フォーム (/edit/
) にリダイレクトします。http.StatusFound
(302) は、リソースが一時的に移動したことを示すHTTPステータスコードです。 return
ステートメントは、リダイレクトが完了した後にそれ以上の処理が行われないようにするために重要です。これにより、存在しないページに対して不必要にテンプレートをレンダリングしようとするのを防ぎます。
2. save
メソッドのエラー処理 (saveHandler
)
saveHandler
は、ユーザーが編集フォームからページを保存しようとしたときに呼び出されます。このコミットでは、Page.save()
メソッドがファイルを書き込む際にエラーが発生した場合の処理が追加されました。
func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
body := r.FormValue("body") // フォームからボディを取得
p := &Page{Title: title, Body: []byte(body)}
err := p.save() // saveメソッドはエラーを返す
if err != nil {
// ファイル保存エラーが発生した場合、Internal Server Errorを返す
http.Error(w, err.Error(), http.StatusInternalServerError)
return // エラー応答後、これ以上処理を行わない
}
http.Redirect(w, r, "/view/"+title, http.StatusFound) // 保存成功後、閲覧ページにリダイレクト
}
err := p.save()
で、ファイルの保存操作の結果として返されるエラーをキャッチします。if err != nil
でエラーをチェックします。- エラーがある場合、
http.Error(w, err.Error(), http.StatusInternalServerError)
を使用して、クライアントに「500 Internal Server Error」を返します。err.Error()
は、error
インターフェースのメソッドで、エラーの詳細な文字列表現を返します。 - これにより、ファイルシステムの問題など、サーバー側で発生した保存エラーをユーザーに適切に通知し、アプリケーションがクラッシュするのを防ぎます。
3. テンプレートレンダリングのエラー処理 (renderTemplate
- index.html
の説明のみ)
index.html
の説明では、renderTemplate
関数におけるテンプレートのパースエラーの処理も示唆されています(ただし、提供された part3-errorhandling.go
には直接含まれていませんが、チュートリアルの最終版では導入される概念です)。
// index.htmlで示唆されるrenderTemplateの改善例
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, err := template.ParseFiles(tmpl + ".html") // エラーをチェック
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = t.Execute(w, p) // 実行時のエラーもチェック
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
template.ParseFiles
やt.Execute
がエラーを返す可能性があるため、それらを適切にチェックし、エラーが発生した場合はhttp.Error
でクライアントに通知します。
4. part3.go
と part3-errorhandling.go
の役割
part3.go
: このファイルは、html/template
の導入と基本的なハンドラの統合を示しますが、エラー処理はまだ不完全です(例:viewHandler
でloadPage
のエラーを_
で無視している)。これは、チュートリアルが段階的に複雑な概念を導入するための「中間ステップ」として機能します。part3-errorhandling.go
: このファイルは、part3.go
を基に、上記で説明したような堅牢なエラーハンドリングのロジックを完全に組み込んだバージョンです。これにより、読者はGo言語でWebアプリケーションを開発する際に、エラーを適切に処理することの重要性と具体的な方法を学ぶことができます。
これらの変更により、Go Wikiチュートリアルは、単なるWebアプリケーションの構築方法だけでなく、Go言語における堅牢なエラーハンドリングのベストプラクティスも教える、より実践的で教育的なリソースへと進化しました。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
- Go Wikiチュートリアル (現在のバージョン): https://go.dev/doc/articles/wiki/ (このコミットが適用された後の状態)
- Go言語におけるエラーハンドリングの公式ブログ記事: https://go.dev/blog/error-handling-and-go
参考にした情報源リンク
- Go Wikiチュートリアルのコミット履歴: https://github.com/golang/go/commits/master/doc/articles/wiki/
- Go言語の
net/http
パッケージドキュメント: https://pkg.go.dev/net/http - Go言語の
html/template
パッケージドキュメント: https://pkg.go.dev/html/template - Go言語の
io/ioutil
パッケージドキュメント: https://pkg.go.dev/io/ioutil - Go言語の
regexp
パッケージドキュメント: https://pkg.go.dev/regexp - Go言語の
errors
パッケージドキュメント: https://pkg.go.dev/errors - HTTPステータスコードに関するMDN Web Docs: https://developer.mozilla.org/ja/docs/Web/HTTP/Status