From 0d50fa11408abbe9b64f1c8df6a8531cb0a4e914 Mon Sep 17 00:00:00 2001 From: Shin Kojima Date: Thu, 26 Dec 2013 22:19:49 +0900 Subject: [PATCH] [ja] keep crlf in original documents --- ja/ebook/01.0.md | 46 +- ja/ebook/02.0.md | 38 +- ja/ebook/02.3.md | 1040 +++++++++++++++++++++---------------------- ja/ebook/02.8.md | 62 +-- ja/ebook/03.0.md | 22 +- ja/ebook/03.1.md | 318 ++++++------- ja/ebook/03.2.md | 132 +++--- ja/ebook/03.5.md | 18 +- ja/ebook/04.0.md | 50 +-- ja/ebook/04.3.md | 136 +++--- ja/ebook/04.4.md | 116 ++--- ja/ebook/04.5.md | 310 ++++++------- ja/ebook/04.6.md | 18 +- ja/ebook/05.0.md | 28 +- ja/ebook/05.3.md | 236 +++++----- ja/ebook/05.7.md | 18 +- ja/ebook/06.0.md | 24 +- ja/ebook/preface.md | 192 ++++---- 18 files changed, 1402 insertions(+), 1402 deletions(-) diff --git a/ja/ebook/01.0.md b/ja/ebook/01.0.md index 835e342a..346e83ef 100644 --- a/ja/ebook/01.0.md +++ b/ja/ebook/01.0.md @@ -1,23 +1,23 @@ -# 1 GOの環境設定 - -Goの世界へようこそ、さっそく初めてみましょう! - -Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にプログラムできる言語です。以下のような特徴を持っています: - -- 一台のコンピュータ上であっという間に大型のGoプログラムを作り出すことができます。 -- Goはソフトウェアの構造にモデルを提供します。分析をより簡単にこなせ、Cスタイルの頭にありがちなファイルとライブラリのincludeの大部分を省くことができます。 -- Goは静的型付け言語です。型には階層がありません。このためユーザは型の定義の関係に時間をとられることなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。 -- Goは完全にガベージコレクションタイプの言語です。また、並列処理とネットワークを基本的にサポートしています。 -- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。 - -Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、モダンにネットワークとマルチプロセッサもサポートしています。これらの目標を達成するには、解決しなければならない言語上の問題がいくつかあります:表現力豊かだけれども軽いシステム、並列処理とガベージコレクション、厳格な依存定義などです。これらはライブラリやツール無しにはうまくいきません。Goもその要望に応えます。 - -この章ではGoのインストール方法と設定について述べます。 - -## 目次 - -![](images/navi1.png?raw=true) - -## links - * [目次]() - * 次へ: [Goのインストール](<01.1.md>) +# 1 GOの環境設定 + +Goの世界へようこそ、さっそく初めてみましょう! + +Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にプログラムできる言語です。以下のような特徴を持っています: + +- 一台のコンピュータ上であっという間に大型のGoプログラムを作り出すことができます。 +- Goはソフトウェアの構造にモデルを提供します。分析をより簡単にこなせ、Cスタイルの頭にありがちなファイルとライブラリのincludeの大部分を省くことができます。 +- Goは静的型付け言語です。型には階層がありません。このためユーザは型の定義の関係に時間をとられることなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。 +- Goは完全にガベージコレクションタイプの言語です。また、並列処理とネットワークを基本的にサポートしています。 +- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。 + +Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、モダンにネットワークとマルチプロセッサもサポートしています。これらの目標を達成するには、解決しなければならない言語上の問題がいくつかあります:表現力豊かだけれども軽いシステム、並列処理とガベージコレクション、厳格な依存定義などです。これらはライブラリやツール無しにはうまくいきません。Goもその要望に応えます。 + +この章ではGoのインストール方法と設定について述べます。 + +## 目次 + +![](images/navi1.png?raw=true) + +## links + * [目次]() + * 次へ: [Goのインストール](<01.1.md>) diff --git a/ja/ebook/02.0.md b/ja/ebook/02.0.md index 498b8fd0..0a101f13 100644 --- a/ja/ebook/02.0.md +++ b/ja/ebook/02.0.md @@ -1,19 +1,19 @@ -# 2 Go言語の基礎 - -GoはCに似たコンパイラ型言語です。ですが、このコンパイル速度は非常に速く、この言語のキーワードもたったの25個です。英文よりも少し少なく勉強するにはかなり簡単です。まずはこれらのキーワードがどのようなものか見てみることにしましょう: - - break default func interface select - case defer go map struct - chan else goto package switch - const fallthrough if range type - continue for import return var - -この章では、この言語の基礎勉強にあなたを連れていきます。各章の紹介を通じて、Goの世界がどれほどまでに簡潔で絶妙にデザインされているかお分かりいただけるはずです。Goを書くことはとても楽しいことです。後から振り返ると、この25個のキーワードがどれだけフレンドリーか理解するはずです。 - -## 目次 -![](images/navi2.png?raw=true) - -## links - * [目次]() - * 前へ: [第一章概要](<01.5.md>) - * 次へ: [こんにちは、Go](<02.1.md>) +# 2 Go言語の基礎 + +GoはCに似たコンパイラ型言語です。ですが、このコンパイル速度は非常に速く、この言語のキーワードもたったの25個です。英文よりも少し少なく勉強するにはかなり簡単です。まずはこれらのキーワードがどのようなものか見てみることにしましょう: + + break default func interface select + case defer go map struct + chan else goto package switch + const fallthrough if range type + continue for import return var + +この章では、この言語の基礎勉強にあなたを連れていきます。各章の紹介を通じて、Goの世界がどれほどまでに簡潔で絶妙にデザインされているかお分かりいただけるはずです。Goを書くことはとても楽しいことです。後から振り返ると、この25個のキーワードがどれだけフレンドリーか理解するはずです。 + +## 目次 +![](images/navi2.png?raw=true) + +## links + * [目次]() + * 前へ: [第一章概要](<01.5.md>) + * 次へ: [こんにちは、Go](<02.1.md>) diff --git a/ja/ebook/02.3.md b/ja/ebook/02.3.md index 12c53d72..fa747ac6 100644 --- a/ja/ebook/02.3.md +++ b/ja/ebook/02.3.md @@ -1,520 +1,520 @@ -# 2.3 フローと関数 -この節ではGoの中のフロー制御と関数操作についてご紹介します。 -## フロー制御 -フロー制御はプログラム言語の中の最も偉大な発明です。なぜならこれがあるだけで、あなたはとても簡単なフローの記述でとても複雑なロジックを表現できるからです。フロー制御は3つの部分から成ります:条件判断、ループ制御及び無条件ジャンプです。 -### if -`if`はあらゆるプログラミング言語の中で最もよく見かけるものかもしれません。この文法は大雑把に言えば:もし条件を満足しなければ何々を行い、そうでなければまたもう一つ別のことをやるということです。 - -Goの中では`if`分岐の文法の中は括弧で括る必要はありません。以下のコードをご覧ください。 - - if x > 10 { - fmt.Println("x is greater than 10") - } else { - fmt.Println("x is less than 10") - } - -Goの`if`はすごいことに、条件分岐の中で変数を宣言できます。この変数のスコープはこの条件ロジックブロック内のみ存在し、他の場所では作用しません。以下に示します - - // 取得値xを計算し、xの大きさを返します。10以上かどうかを判断します。 - if x := computedValue(); x > 10 { - fmt.Println("x is greater than 10") - } else { - fmt.Println("x is less than 10") - } - - //ここではもしこのようにコールしてしまうとコンパイルエラーとなります。xは条件の中の変数だからです。 - fmt.Println(x) - -この条件の時は以下のようになります: - - if integer == 3 { - fmt.Println("The integer is equal to 3") - } else if integer < 3 { - fmt.Println("The integer is less than 3") - } else { - fmt.Println("The integer is greater than 3") - } - -### goto - -Goには`goto`句があります- - ぜひ賢く使ってください。`goto`は必ず事前に関数内で定義したタグにジャンプします。例えばこのようなループがあったと仮定します: - - func myFunc() { - i := 0 - Here: //この行の最初の単語はコロンを最後に持ってくることでタグとなります。 - println(i) - i++ - goto Here //Hereにジャンプします。 - } - ->タグの名前は大文字小文字を区別します。 - -### for -Goにで最も協力なロジックコントロールといえば、`for`です。これはループでデータを読むのに使えます。`while`でロジックをコントロールしても構いません。イテレーション操作も行えます。文法は以下の通りです: - - for expression1; expression2; expression3 { - //... - } - -`expression1`、`expression2`と`expression3`はどれも式です。この中で`expression1`と`expression3`は変数宣言または関数のコールの戻り値のようなものです。`expression2`は条件判断に用いられます。`expression1`はループの開始前にコールされます。`expression3`は毎回ループする際の終了時にコールされます。 - -だらだら喋るよりも例を見たほうが早いでしょう。以下に例を示します: - - package main - import "fmt" - - func main(){ - sum := 0; - for index:=0; index < 10 ; index++ { - sum += index - } - fmt.Println("sum is equal to ", sum) - } - // 出力:sum is equal to 45 - -時々複数の代入操作を行いたい時があります。Goのなかには`,`という演算子はないので、平行して代入することができます。`i, j = i+1, j-1` - - -時々`expression1`と`expression3`を省略します: - - sum := 1 - for ; sum < 1000; { - sum += sum - } - -この中で`;`は省略することができます。ですので下のようなコードになります。どこかで見た覚えはありませんか?そう、これは`while`の機能です。 - - sum := 1 - for sum < 1000 { - sum += sum - } - -ループの中では`break`と`continue`という2つのキーとなる操作があります。`break`操作は現在のループから抜け出します。`continue`は次のループに飛び越えます。ネストが深い場合、`break`はタグと組み合わせて使用することができます。つまり、タグが指定する位置までジャンプすることになります。詳細は以下の例をご覧ください。 - - for index := 10; index>0; index-- { - if index == 5{ - break // またはcontinue - } - fmt.Println(index) - } - // breakであれば10、9、8、7、6が出力されます。 - // continueの場合は10、9、8、7、6、4、3、2、1が出力されます。 - -`break`と`continue`はタグを添えることができます。複数ネストしたループで外側のループからジャンプする際に使用されます。 - -`for`は`range`と組み合わせて`slice`と`map`のデータを読み込むことができます: - - for k,v:=range map { - fmt.Println("map's key:",k) - fmt.Println("map's val:",v) - } - -Goは"複数の戻り値"をサポートしていますが、"宣言して使用されていない"変数に対してコンパイラはエラーを出力します。このような状況では`_`を使って必要のない戻り値を捨てる事ができます。 -例えば - - for _, v := range map{ - fmt.Println("map's val:", v) - } - - -### switch -時々たくさんの`if-else`を書くことでロジック処理を行いたくなるかもしれません。コードは非常に醜く冗長になります。またメンテナンスも容易ではなくなるので、`switch`を使って解決することができます。この文法は以下のようなものです - - switch sExpr { - case expr1: - some instructions - case expr2: - some other instructions - case expr3: - some other instructions - default: - other code - } - -`sExpr`と`expr1`、`expr2`、`expr3`の型は一致させる必要があります。Goの`switch`は非常に使い勝手がよく、式は必ずしも定数や整数である必要はありません。実行のプロセスは上から下まで、マッチする項目が見つかるまで行われます。もし`switch`に式がなければ、`true`とマッチします。 - - i := 10 - switch i { - case 1: - fmt.Println("i is equal to 1") - case 2, 3, 4: - fmt.Println("i is equal to 2, 3 or 4") - case 10: - fmt.Println("i is equal to 10") - default: - fmt.Println("All I know is that i is an integer") - } - -5行目で、いくつもの値を`case`の中に集めています。また同時に、Goの`switch`はデフォルトで`case`の最後に`break`があることになっているので、マッチに成功した後は他のcaseが実行されることはなく、`switch`全体から抜け出します。ただし、`fallthrough`を使用することであとに続くcaseコードを強制的に実行させることができます。 - - integer := 6 - switch integer { - case 4: - fmt.Println("The integer was <= 4") - fallthrough - case 5: - fmt.Println("The integer was <= 5") - fallthrough - case 6: - fmt.Println("The integer was <= 6") - fallthrough - case 7: - fmt.Println("The integer was <= 7") - fallthrough - case 8: - fmt.Println("The integer was <= 8") - fallthrough - default: - fmt.Println("default case") - } - -上のプログラムは以下のように出力します - - The integer was <= 6 - The integer was <= 7 - The integer was <= 8 - default case - - -## 関数 -関数はGoの中心的な設計です。キーワード`func`によって宣言します。形式は以下の通り: - - func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { - //ここはロジック処理のコードです。 - //複数の値を戻り値とします。 - return value1, value2 - } - -上のコードから次のようなことが分かります - -- キーワード`func`で`funcName`という名前の関数を宣言します。 -- 関数はひとつまたは複数の引数をとることができ、各引数の後には型が続きます。`,`をデリミタとします。 -- 関数は複数の戻り値を持ってかまいません。 -- 上の戻り値は2つの変数`output1`と`output2`であると宣言されています。もしあなたが宣言したくないというのであればそれでもかみません。直接2つの型です。 -- もしひとつの戻り値しか存在せず、また戻り値の変数が宣言されていなかった場合、戻り値の括弧を省略することができます。 -- もし戻り値が無ければ、最後の戻り値の情報も省略することができます。 -- もし戻り値があれば、関数の中でreturn文を追加する必要があります。 - -以下では実際に関数の例を応用しています(Maxの値を計算します) - - package main - import "fmt" - - // a、bの中から最大値を返します。 - func max(a, b int) int { - if a > b { - return a - } - return b - } - - func main() { - x := 3 - y := 4 - z := 5 - - max_xy := max(x, y) //関数max(x, y)をコール - max_xz := max(x, z) //関数max(x, z)をコール - - fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) - fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) - fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 直接コールしてもかまいません。 - } - -上では`max`関数に2つの引数があることがわかります。この型はどれも`int`です。第一引数の型は省略することができます(つまり、a,b int,でありa int, b intではありません)、デフォルトは直近の型です。2つ以上の同じ型の変数または戻り値も同じです。同時に戻り値がひとつであることに注意してください。これは省略記法です。 - -### 複数の戻り値 -Go言語はCに比べ先進的な特徴を持っています。関数が複数の戻り値を持てるのもその一つです。 - -コードの例を見てみましょう - - package main - import "fmt" - - //A+B と A*B を返します - func SumAndProduct(A, B int) (int, int) { - return A+B, A*B - } - - func main() { - x := 3 - y := 4 - - xPLUSy, xTIMESy := SumAndProduct(x, y) - - fmt.Printf("%d + %d = %d\n", x, y, xPLUSy) - fmt.Printf("%d * %d = %d\n", x, y, xTIMESy) - } - -上の例では直接2つの引数を返しました。当然引数を返す変数に命名してもかまいません。この例では2つの型のみ使っていますが、下のように定義することもできます。値が返る際は変数名を付けなくてかまいません。なぜなら関数の中で直接初期化されているからです。しかしもしあなたの関数がエクスポートされるのであれば(大文字からはじまります)オフィシャルではなるべく戻り値に名前をつけるようお勧めしています。なぜなら名前のわからない戻り値はコードをより簡潔なものにしますが、生成されるドキュメントの可読性がひどくなるからです。 - - func SumAndProduct(A, B int) (add int, Multiplied int) { - add = A+B - Multiplied = A*B - return - } - -### 可変長引数 -Goの関数は可変長引数をサポートしています。可変長引数を受け付ける関数は不特定多数の引数があります。これを実現するために、関数が可変長引数を受け取れるよう定義する必要があります: - - func myfunc(arg ...int) {} -`arg ...int`はGoにこの関数が不特定多数の引数を受け付けることを伝えます。ご注意ください。この引数の型はすべて`int`です。関数ブロックの中で変数`arg`は`int`の`slice`となります。 - - for _, n := range arg { - fmt.Printf("And the number is: %d\n", n) - } - -### 値渡しと参照渡し -引数をコールされる関数の中に渡すとき、実際にはこの値のコピーが渡されます。コールされる関数の中で引数に修正をくわえても、関数をコールした実引き数には何の変化もありません。数値の変化はコピーの上で行われるだけだからです。 - -この内容を検証するために、ひとつ例を見てみましょう - - package main - import "fmt" - - //引数+1を行う、簡単な関数 - func add1(a int) int { - a = a+1 // aの値を変更します。 - return a //新しい値を返します。 - } - - func main() { - x := 3 - - fmt.Println("x = ", x) // "x = 3"と出力するはずです。 - - x1 := add1(x) //add1(x) をコールします。 - - fmt.Println("x+1 = ", x1) // "x+1 = 4" と出力するはずです。 - fmt.Println("x = ", x) // "x = 3" と出力するはずです。 - } - -どうです?`add1`関数をコールし、`add1`のなかで`a = a+1`の操作を実行したとしても、上述の`x`変数には何の変化も発生しません。 - -理由はとても簡単です:`add1`がコールされた際、`add1`が受け取る引数は`x`そのものではなく、`x`のコピーだからです。 - -もし本当にこの`x`そのものを渡したくなったらどうするの?と疑問に思うかもしれません。 - -この場合いわゆるポインタにまで話がつながります。我々は変数がメモリの中のある特定の位置に存在していることを知っています。変数を修正するということはとどのつまり変数のアドレスにあるメモリを修正していることになります。`add1`関数が`x`変数のアドレスを知ってさえいれば、`x`変数の値を変更することが可能です。そのため、我々は`x`の存在するアドレスである`&x`を関数に渡し、関数の変数の型を`int`からポインタ変数である`*int`に変更します。これで関数の中で`x`の値を変更することができるようになりました。この時関数は依然としてコピーにより引数を受け渡しますが、コピーしているのはポインタになります。以下の例をご覧ください。 - - package main - import "fmt" - - //引数に+1を行う簡単な関数 - func add1(a *int) int { // ご注意ください。 - *a = *a+1 // aの値を修正しています。 - return *a // 新しい値を返します。 - } - - func main() { - x := 3 - - fmt.Println("x = ", x) // "x = 3"と出力するはずです。 - - x1 := add1(&x) // add1(&x) をコールしてxのアドレスを渡します。 - - fmt.Println("x+1 = ", x1) // "x+1 = 4"を出力するはずです。 - fmt.Println("x = ", x) // "x = 4"を出力するはずです。 - } - -このように`x`を修正するという目的に到達しました。では、ポインタを渡す長所はなんなのでしょうか? - -- ポインタを渡すことで複数の関数が同じオブジェクトに対して操作を行うことができます。 -- ポインタ渡しは比較的軽いです(8バイト)、ただのメモリのアドレスです。ポインタを使って大きな構造体を渡すことができます。もし値渡しを行なっていたら、相対的にもっと多くのシステムリソース(メモリと時間)を毎回のコピーで消費することになります。そのため大きな構造体を渡す際は、ポインタを使うのが賢い選択というものです。 -- Go言語の`string`、`slice`、`map`の3つの型はメカニズムを実現するポインタのようなものです。ですので、直接渡すことができますので、アドレスを取得してポインタを渡す必要はありません。(注:もし関数が`slice`の長さを変更する場合はアドレスを取得し、ポインタを渡す必要があります。) - -### defer -Go言語のすばらしいデザインの中に、遅延(defer)文法があります。関数の中でdefer文を複数追加することができます。関数が最後まで実行された時、このdefer文が逆順に実行されます。最後にこの関数が返ります。特に、リソースをオープンする操作を行なっているようなとき、エラーの発生に対してロールバックし、必要なリソースをクローズする必要があるかと思います。さもなければとても簡単にリソースのリークといった問題を引き起こすことになります。我々はリソースを開く際は一般的に以下のようにします: - - func ReadWrite() bool { - file.Open("file") - // 何かを行う - if failureX { - file.Close() - return false - } - - if failureY { - file.Close() - return false - } - - file.Close() - return true - } - -上のコードはとても多くの重複がみられます。Goの`defer`はこの問題を解決します。これを使用した後、コードは減るばかりでなく、プログラムもよりエレガントになります。`defer`の後に指定された関数が関数を抜ける前にコールされます。 - - func ReadWrite() bool { - file.Open("file") - defer file.Close() - if failureX { - return false - } - if failureY { - return false - } - return true - } - -もし`defer`を多用する場合は、`defer`はLIFOモードが採用されます。そのため、以下のコードは`4 3 2 1 0`を出力します。 - - for i := 0; i < 5; i++ { - defer fmt.Printf("%d ", i) - } - -### 値、型としての関数 - -Goでは関数も変数の一種です。`type`を通して定義します。これは全て同じ引数と同じ戻り値を持つ一つの型です。 - - type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...]) - -関すを型として扱うことにメリットはあるのでしょうか?ではこの型の関数を値として渡してみましょう。以下の例をご覧ください。 - - package main - import "fmt" - - type testInt func(int) bool // 関数の型を宣言します。 - - func isOdd(integer int) bool { - if integer%2 == 0 { - return false - } - return true - } - - func isEven(integer int) bool { - if integer%2 == 0 { - return true - } - return false - } - - // ここでは宣言する関数の型を引数のひとつとみなします。 - - func filter(slice []int, f testInt) []int { - var result []int - for _, value := range slice { - if f(value) { - result = append(result, value) - } - } - return result - } - - func main(){ - slice := []int {1, 2, 3, 4, 5, 7} - fmt.Println("slice = ", slice) - odd := filter(slice, isOdd) // 関数の値渡し - fmt.Println("Odd elements of slice are: ", odd) - even := filter(slice, isEven) // 関数の値渡し - fmt.Println("Even elements of slice are: ", even) - } - -共有のインターフェースを書くときに関数を値と型にみなすのは非常に便利です。上の例で`testInt`という型は関数の型の一つでした。ふたつの`filter`関数の引数と戻り値は`testInt`の型と同じですが、より多くのロジックを実現することができます。このように我々のプログラムをより優れたものにすることができます。 - -### PanicとRecover - -GoにはJavaのような例外処理はありません。例外を投げないのです。その代わり、`panic`と`recover`を使用します。ぜひ覚えておいてください、これは最後の手段として使うことを。つまり、あなたのコードにあってはなりません。もしくは`panic`を極力減らしてください。これは非常に協力なツールです。賢く使ってください。では、どのように使うのでしょうか? - -Panic ->ビルトイン関数です。オリジナルの処理フローを中断させることができます。パニックが発生するフローの中に入って関数`F`が`panic`をコールします。このプロセスは継続して実行されます。一旦`panic`の`goroutine`が発生すると、コールされた関数がすべて返ります。この時プログラムを抜けます。パニックは直接`panic`をコールします。実行時にエラーを発生させてもかまいません。例えば配列の境界を超えてアクセスする、などです。 - -Recover ->ビルトイン関数です。パニックを発生させるフローの`goroutine`を復元することができます。`recover`は遅延関数の中でのみ有効です。通常の実行中、`recover`をコールすると`nil`が返ります。他には何の効果もありません。もし現在の`goroutine`がパニックに陥ったら`recover`をコールして、`panic`の入力値を補足し、正常な実行に復元することができます。 - -下の関数のフローの中でどのように`panic`を使うかご覧ください - - var user = os.Getenv("USER") - - func init() { - if user == "" { - panic("no value for $USER") - } - } - -この関数は引数となっている関数が実行時に`panic`を発生するか検査します: - - func throwsPanic(f func()) (b bool) { - defer func() { - if x := recover(); x != nil { - b = true - } - }() - f() //関数fを実行します。もしfの中でpanicが出現したら、復元を行うことができます。 - return - } - -### `main`関数と`init`関数 - -Goでは2つの関数が予約されています:`init`関数(すべての`package`で使用できます)と`main`関数(`package main`でしか使用できません)です。この2つの関数は定義される際いかなる引数と戻り値も持ちません。`package`のなかで複数の`init`関数を書いたとしても、もちろん可読性か後々のメンテナンス性に対してですが、`package`の中では各ファイルに一つだけの`init`関数を書くよう強くおすすめします。 - -Goのプログラムは自動で`init()`と`main()`をコールしますので、どこかでこの2つの関数をコールする必要はありません。各`package`の`init`関数はオプションです。しかし`package main`は必ず一つ`main`関数を含まなければなりません。 - -プログラムの初期化と実行はすべて`main`パッケージから始まります。もし`main`パッケージが他のパッケージをインポートしていたら、コンパイル時にその依存パッケージがインポートされます。あるパッケージが複数のパッケージに同時にインポートされている場合は、先にその他のパッケージがインポートされ、その後このパッケージの中にあるパッケージクラス定数と変数が初期化されます。次にinit関数が(もしあれば)実行され、最後に`main`関数が実行されます。以下の図で実行過程を詳しくご説明しています。 - -![](images/2.3.init.png?raw=true) - -図2.6 main関数によるパッケージのインポートと初期化過程の図 - -### import -Goのコードを書いている時は、importコマンドによってパッケージファイルをインポートすることがよくあります。私達が通常使う方法は以下を参考にしてください: - - import( - "fmt" - ) - -その後コードの中では以下のような方法でコールすることができます。 - - fmt.Println("hello world") - -上のfmtはGo言語の標準ライブラリです。実はgorootの下にこのモジュールが加えられています。当然Goのインポートは以下のような2つの方法で自分の書いたモジュールを追加することができます: - -1. 相対パス - - import "./model" //カレントファイルと同じディレクトリにあるmodelディレクトリ、ただし、この方法によるimportはおすすめしません。 - -2. 絶対パス - - import "shorturl/model" //gopath/src/shorturl/modelモジュールを追加します。 - - -ここではimportの通常のいくつかの方法をご説明しました。ただ他にも特殊なimportがあります。新人を悩ませる方法ですが、ここでは一つ一つ一体何がどうなっているのかご説明しましょう - - -1. ドット操作 - - 時々、以下のようなパッケージのインポート方法を見ることがあります - - import( - . "fmt" - ) - - このドット操作の意味はこのパッケージがインポートされた後このパッケージの関数をコールする際、パッケージ名を省略することができます。つまり、前であなたがコールしたようなfmt.Println("hello world")はPrintln("hello world")というように省略することができます。 - -2. エイリアス操作 - - エイリアス操作はその名の通りパッケージ名に他の覚えやすい名前をつけることができます。 - - import( - f "fmt" - ) - - エイリアス操作の場合パッケージ関数をコールする際プレフィックスが自分たちのものになります。すなわち、f.Println("hello world") - -3. _操作 - - この操作は通常とても理解しづらい方法です。以下のimportをご覧ください。 - - import ( - "database/sql" - _ "github.com/ziutek/mymysql/godrv" - ) - - _操作はこのパッケージをインポートするだけでパッケージの中の関数を直接使うわけではなく、このパッケージの中にあるinit関数をコールします。 - - -## links - * [目次]() - * 前へ: [Goの基礎](<02.2.md>) - * 次へ: [struct型](<02.4.md>) +# 2.3 フローと関数 +この節ではGoの中のフロー制御と関数操作についてご紹介します。 +## フロー制御 +フロー制御はプログラム言語の中の最も偉大な発明です。なぜならこれがあるだけで、あなたはとても簡単なフローの記述でとても複雑なロジックを表現できるからです。フロー制御は3つの部分から成ります:条件判断、ループ制御及び無条件ジャンプです。 +### if +`if`はあらゆるプログラミング言語の中で最もよく見かけるものかもしれません。この文法は大雑把に言えば:もし条件を満足しなければ何々を行い、そうでなければまたもう一つ別のことをやるということです。 + +Goの中では`if`分岐の文法の中は括弧で括る必要はありません。以下のコードをご覧ください。 + + if x > 10 { + fmt.Println("x is greater than 10") + } else { + fmt.Println("x is less than 10") + } + +Goの`if`はすごいことに、条件分岐の中で変数を宣言できます。この変数のスコープはこの条件ロジックブロック内のみ存在し、他の場所では作用しません。以下に示します + + // 取得値xを計算し、xの大きさを返します。10以上かどうかを判断します。 + if x := computedValue(); x > 10 { + fmt.Println("x is greater than 10") + } else { + fmt.Println("x is less than 10") + } + + //ここではもしこのようにコールしてしまうとコンパイルエラーとなります。xは条件の中の変数だからです。 + fmt.Println(x) + +この条件の時は以下のようになります: + + if integer == 3 { + fmt.Println("The integer is equal to 3") + } else if integer < 3 { + fmt.Println("The integer is less than 3") + } else { + fmt.Println("The integer is greater than 3") + } + +### goto + +Goには`goto`句があります- - ぜひ賢く使ってください。`goto`は必ず事前に関数内で定義したタグにジャンプします。例えばこのようなループがあったと仮定します: + + func myFunc() { + i := 0 + Here: //この行の最初の単語はコロンを最後に持ってくることでタグとなります。 + println(i) + i++ + goto Here //Hereにジャンプします。 + } + +>タグの名前は大文字小文字を区別します。 + +### for +Goにで最も協力なロジックコントロールといえば、`for`です。これはループでデータを読むのに使えます。`while`でロジックをコントロールしても構いません。イテレーション操作も行えます。文法は以下の通りです: + + for expression1; expression2; expression3 { + //... + } + +`expression1`、`expression2`と`expression3`はどれも式です。この中で`expression1`と`expression3`は変数宣言または関数のコールの戻り値のようなものです。`expression2`は条件判断に用いられます。`expression1`はループの開始前にコールされます。`expression3`は毎回ループする際の終了時にコールされます。 + +だらだら喋るよりも例を見たほうが早いでしょう。以下に例を示します: + + package main + import "fmt" + + func main(){ + sum := 0; + for index:=0; index < 10 ; index++ { + sum += index + } + fmt.Println("sum is equal to ", sum) + } + // 出力:sum is equal to 45 + +時々複数の代入操作を行いたい時があります。Goのなかには`,`という演算子はないので、平行して代入することができます。`i, j = i+1, j-1` + + +時々`expression1`と`expression3`を省略します: + + sum := 1 + for ; sum < 1000; { + sum += sum + } + +この中で`;`は省略することができます。ですので下のようなコードになります。どこかで見た覚えはありませんか?そう、これは`while`の機能です。 + + sum := 1 + for sum < 1000 { + sum += sum + } + +ループの中では`break`と`continue`という2つのキーとなる操作があります。`break`操作は現在のループから抜け出します。`continue`は次のループに飛び越えます。ネストが深い場合、`break`はタグと組み合わせて使用することができます。つまり、タグが指定する位置までジャンプすることになります。詳細は以下の例をご覧ください。 + + for index := 10; index>0; index-- { + if index == 5{ + break // またはcontinue + } + fmt.Println(index) + } + // breakであれば10、9、8、7、6が出力されます。 + // continueの場合は10、9、8、7、6、4、3、2、1が出力されます。 + +`break`と`continue`はタグを添えることができます。複数ネストしたループで外側のループからジャンプする際に使用されます。 + +`for`は`range`と組み合わせて`slice`と`map`のデータを読み込むことができます: + + for k,v:=range map { + fmt.Println("map's key:",k) + fmt.Println("map's val:",v) + } + +Goは"複数の戻り値"をサポートしていますが、"宣言して使用されていない"変数に対してコンパイラはエラーを出力します。このような状況では`_`を使って必要のない戻り値を捨てる事ができます。 +例えば + + for _, v := range map{ + fmt.Println("map's val:", v) + } + + +### switch +時々たくさんの`if-else`を書くことでロジック処理を行いたくなるかもしれません。コードは非常に醜く冗長になります。またメンテナンスも容易ではなくなるので、`switch`を使って解決することができます。この文法は以下のようなものです + + switch sExpr { + case expr1: + some instructions + case expr2: + some other instructions + case expr3: + some other instructions + default: + other code + } + +`sExpr`と`expr1`、`expr2`、`expr3`の型は一致させる必要があります。Goの`switch`は非常に使い勝手がよく、式は必ずしも定数や整数である必要はありません。実行のプロセスは上から下まで、マッチする項目が見つかるまで行われます。もし`switch`に式がなければ、`true`とマッチします。 + + i := 10 + switch i { + case 1: + fmt.Println("i is equal to 1") + case 2, 3, 4: + fmt.Println("i is equal to 2, 3 or 4") + case 10: + fmt.Println("i is equal to 10") + default: + fmt.Println("All I know is that i is an integer") + } + +5行目で、いくつもの値を`case`の中に集めています。また同時に、Goの`switch`はデフォルトで`case`の最後に`break`があることになっているので、マッチに成功した後は他のcaseが実行されることはなく、`switch`全体から抜け出します。ただし、`fallthrough`を使用することであとに続くcaseコードを強制的に実行させることができます。 + + integer := 6 + switch integer { + case 4: + fmt.Println("The integer was <= 4") + fallthrough + case 5: + fmt.Println("The integer was <= 5") + fallthrough + case 6: + fmt.Println("The integer was <= 6") + fallthrough + case 7: + fmt.Println("The integer was <= 7") + fallthrough + case 8: + fmt.Println("The integer was <= 8") + fallthrough + default: + fmt.Println("default case") + } + +上のプログラムは以下のように出力します + + The integer was <= 6 + The integer was <= 7 + The integer was <= 8 + default case + + +## 関数 +関数はGoの中心的な設計です。キーワード`func`によって宣言します。形式は以下の通り: + + func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { + //ここはロジック処理のコードです。 + //複数の値を戻り値とします。 + return value1, value2 + } + +上のコードから次のようなことが分かります + +- キーワード`func`で`funcName`という名前の関数を宣言します。 +- 関数はひとつまたは複数の引数をとることができ、各引数の後には型が続きます。`,`をデリミタとします。 +- 関数は複数の戻り値を持ってかまいません。 +- 上の戻り値は2つの変数`output1`と`output2`であると宣言されています。もしあなたが宣言したくないというのであればそれでもかみません。直接2つの型です。 +- もしひとつの戻り値しか存在せず、また戻り値の変数が宣言されていなかった場合、戻り値の括弧を省略することができます。 +- もし戻り値が無ければ、最後の戻り値の情報も省略することができます。 +- もし戻り値があれば、関数の中でreturn文を追加する必要があります。 + +以下では実際に関数の例を応用しています(Maxの値を計算します) + + package main + import "fmt" + + // a、bの中から最大値を返します。 + func max(a, b int) int { + if a > b { + return a + } + return b + } + + func main() { + x := 3 + y := 4 + z := 5 + + max_xy := max(x, y) //関数max(x, y)をコール + max_xz := max(x, z) //関数max(x, z)をコール + + fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) + fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) + fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 直接コールしてもかまいません。 + } + +上では`max`関数に2つの引数があることがわかります。この型はどれも`int`です。第一引数の型は省略することができます(つまり、a,b int,でありa int, b intではありません)、デフォルトは直近の型です。2つ以上の同じ型の変数または戻り値も同じです。同時に戻り値がひとつであることに注意してください。これは省略記法です。 + +### 複数の戻り値 +Go言語はCに比べ先進的な特徴を持っています。関数が複数の戻り値を持てるのもその一つです。 + +コードの例を見てみましょう + + package main + import "fmt" + + //A+B と A*B を返します + func SumAndProduct(A, B int) (int, int) { + return A+B, A*B + } + + func main() { + x := 3 + y := 4 + + xPLUSy, xTIMESy := SumAndProduct(x, y) + + fmt.Printf("%d + %d = %d\n", x, y, xPLUSy) + fmt.Printf("%d * %d = %d\n", x, y, xTIMESy) + } + +上の例では直接2つの引数を返しました。当然引数を返す変数に命名してもかまいません。この例では2つの型のみ使っていますが、下のように定義することもできます。値が返る際は変数名を付けなくてかまいません。なぜなら関数の中で直接初期化されているからです。しかしもしあなたの関数がエクスポートされるのであれば(大文字からはじまります)オフィシャルではなるべく戻り値に名前をつけるようお勧めしています。なぜなら名前のわからない戻り値はコードをより簡潔なものにしますが、生成されるドキュメントの可読性がひどくなるからです。 + + func SumAndProduct(A, B int) (add int, Multiplied int) { + add = A+B + Multiplied = A*B + return + } + +### 可変長引数 +Goの関数は可変長引数をサポートしています。可変長引数を受け付ける関数は不特定多数の引数があります。これを実現するために、関数が可変長引数を受け取れるよう定義する必要があります: + + func myfunc(arg ...int) {} +`arg ...int`はGoにこの関数が不特定多数の引数を受け付けることを伝えます。ご注意ください。この引数の型はすべて`int`です。関数ブロックの中で変数`arg`は`int`の`slice`となります。 + + for _, n := range arg { + fmt.Printf("And the number is: %d\n", n) + } + +### 値渡しと参照渡し +引数をコールされる関数の中に渡すとき、実際にはこの値のコピーが渡されます。コールされる関数の中で引数に修正をくわえても、関数をコールした実引き数には何の変化もありません。数値の変化はコピーの上で行われるだけだからです。 + +この内容を検証するために、ひとつ例を見てみましょう + + package main + import "fmt" + + //引数+1を行う、簡単な関数 + func add1(a int) int { + a = a+1 // aの値を変更します。 + return a //新しい値を返します。 + } + + func main() { + x := 3 + + fmt.Println("x = ", x) // "x = 3"と出力するはずです。 + + x1 := add1(x) //add1(x) をコールします。 + + fmt.Println("x+1 = ", x1) // "x+1 = 4" と出力するはずです。 + fmt.Println("x = ", x) // "x = 3" と出力するはずです。 + } + +どうです?`add1`関数をコールし、`add1`のなかで`a = a+1`の操作を実行したとしても、上述の`x`変数には何の変化も発生しません。 + +理由はとても簡単です:`add1`がコールされた際、`add1`が受け取る引数は`x`そのものではなく、`x`のコピーだからです。 + +もし本当にこの`x`そのものを渡したくなったらどうするの?と疑問に思うかもしれません。 + +この場合いわゆるポインタにまで話がつながります。我々は変数がメモリの中のある特定の位置に存在していることを知っています。変数を修正するということはとどのつまり変数のアドレスにあるメモリを修正していることになります。`add1`関数が`x`変数のアドレスを知ってさえいれば、`x`変数の値を変更することが可能です。そのため、我々は`x`の存在するアドレスである`&x`を関数に渡し、関数の変数の型を`int`からポインタ変数である`*int`に変更します。これで関数の中で`x`の値を変更することができるようになりました。この時関数は依然としてコピーにより引数を受け渡しますが、コピーしているのはポインタになります。以下の例をご覧ください。 + + package main + import "fmt" + + //引数に+1を行う簡単な関数 + func add1(a *int) int { // ご注意ください。 + *a = *a+1 // aの値を修正しています。 + return *a // 新しい値を返します。 + } + + func main() { + x := 3 + + fmt.Println("x = ", x) // "x = 3"と出力するはずです。 + + x1 := add1(&x) // add1(&x) をコールしてxのアドレスを渡します。 + + fmt.Println("x+1 = ", x1) // "x+1 = 4"を出力するはずです。 + fmt.Println("x = ", x) // "x = 4"を出力するはずです。 + } + +このように`x`を修正するという目的に到達しました。では、ポインタを渡す長所はなんなのでしょうか? + +- ポインタを渡すことで複数の関数が同じオブジェクトに対して操作を行うことができます。 +- ポインタ渡しは比較的軽いです(8バイト)、ただのメモリのアドレスです。ポインタを使って大きな構造体を渡すことができます。もし値渡しを行なっていたら、相対的にもっと多くのシステムリソース(メモリと時間)を毎回のコピーで消費することになります。そのため大きな構造体を渡す際は、ポインタを使うのが賢い選択というものです。 +- Go言語の`string`、`slice`、`map`の3つの型はメカニズムを実現するポインタのようなものです。ですので、直接渡すことができますので、アドレスを取得してポインタを渡す必要はありません。(注:もし関数が`slice`の長さを変更する場合はアドレスを取得し、ポインタを渡す必要があります。) + +### defer +Go言語のすばらしいデザインの中に、遅延(defer)文法があります。関数の中でdefer文を複数追加することができます。関数が最後まで実行された時、このdefer文が逆順に実行されます。最後にこの関数が返ります。特に、リソースをオープンする操作を行なっているようなとき、エラーの発生に対してロールバックし、必要なリソースをクローズする必要があるかと思います。さもなければとても簡単にリソースのリークといった問題を引き起こすことになります。我々はリソースを開く際は一般的に以下のようにします: + + func ReadWrite() bool { + file.Open("file") + // 何かを行う + if failureX { + file.Close() + return false + } + + if failureY { + file.Close() + return false + } + + file.Close() + return true + } + +上のコードはとても多くの重複がみられます。Goの`defer`はこの問題を解決します。これを使用した後、コードは減るばかりでなく、プログラムもよりエレガントになります。`defer`の後に指定された関数が関数を抜ける前にコールされます。 + + func ReadWrite() bool { + file.Open("file") + defer file.Close() + if failureX { + return false + } + if failureY { + return false + } + return true + } + +もし`defer`を多用する場合は、`defer`はLIFOモードが採用されます。そのため、以下のコードは`4 3 2 1 0`を出力します。 + + for i := 0; i < 5; i++ { + defer fmt.Printf("%d ", i) + } + +### 値、型としての関数 + +Goでは関数も変数の一種です。`type`を通して定義します。これは全て同じ引数と同じ戻り値を持つ一つの型です。 + + type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...]) + +関すを型として扱うことにメリットはあるのでしょうか?ではこの型の関数を値として渡してみましょう。以下の例をご覧ください。 + + package main + import "fmt" + + type testInt func(int) bool // 関数の型を宣言します。 + + func isOdd(integer int) bool { + if integer%2 == 0 { + return false + } + return true + } + + func isEven(integer int) bool { + if integer%2 == 0 { + return true + } + return false + } + + // ここでは宣言する関数の型を引数のひとつとみなします。 + + func filter(slice []int, f testInt) []int { + var result []int + for _, value := range slice { + if f(value) { + result = append(result, value) + } + } + return result + } + + func main(){ + slice := []int {1, 2, 3, 4, 5, 7} + fmt.Println("slice = ", slice) + odd := filter(slice, isOdd) // 関数の値渡し + fmt.Println("Odd elements of slice are: ", odd) + even := filter(slice, isEven) // 関数の値渡し + fmt.Println("Even elements of slice are: ", even) + } + +共有のインターフェースを書くときに関数を値と型にみなすのは非常に便利です。上の例で`testInt`という型は関数の型の一つでした。ふたつの`filter`関数の引数と戻り値は`testInt`の型と同じですが、より多くのロジックを実現することができます。このように我々のプログラムをより優れたものにすることができます。 + +### PanicとRecover + +GoにはJavaのような例外処理はありません。例外を投げないのです。その代わり、`panic`と`recover`を使用します。ぜひ覚えておいてください、これは最後の手段として使うことを。つまり、あなたのコードにあってはなりません。もしくは`panic`を極力減らしてください。これは非常に協力なツールです。賢く使ってください。では、どのように使うのでしょうか? + +Panic +>ビルトイン関数です。オリジナルの処理フローを中断させることができます。パニックが発生するフローの中に入って関数`F`が`panic`をコールします。このプロセスは継続して実行されます。一旦`panic`の`goroutine`が発生すると、コールされた関数がすべて返ります。この時プログラムを抜けます。パニックは直接`panic`をコールします。実行時にエラーを発生させてもかまいません。例えば配列の境界を超えてアクセスする、などです。 + +Recover +>ビルトイン関数です。パニックを発生させるフローの`goroutine`を復元することができます。`recover`は遅延関数の中でのみ有効です。通常の実行中、`recover`をコールすると`nil`が返ります。他には何の効果もありません。もし現在の`goroutine`がパニックに陥ったら`recover`をコールして、`panic`の入力値を補足し、正常な実行に復元することができます。 + +下の関数のフローの中でどのように`panic`を使うかご覧ください + + var user = os.Getenv("USER") + + func init() { + if user == "" { + panic("no value for $USER") + } + } + +この関数は引数となっている関数が実行時に`panic`を発生するか検査します: + + func throwsPanic(f func()) (b bool) { + defer func() { + if x := recover(); x != nil { + b = true + } + }() + f() //関数fを実行します。もしfの中でpanicが出現したら、復元を行うことができます。 + return + } + +### `main`関数と`init`関数 + +Goでは2つの関数が予約されています:`init`関数(すべての`package`で使用できます)と`main`関数(`package main`でしか使用できません)です。この2つの関数は定義される際いかなる引数と戻り値も持ちません。`package`のなかで複数の`init`関数を書いたとしても、もちろん可読性か後々のメンテナンス性に対してですが、`package`の中では各ファイルに一つだけの`init`関数を書くよう強くおすすめします。 + +Goのプログラムは自動で`init()`と`main()`をコールしますので、どこかでこの2つの関数をコールする必要はありません。各`package`の`init`関数はオプションです。しかし`package main`は必ず一つ`main`関数を含まなければなりません。 + +プログラムの初期化と実行はすべて`main`パッケージから始まります。もし`main`パッケージが他のパッケージをインポートしていたら、コンパイル時にその依存パッケージがインポートされます。あるパッケージが複数のパッケージに同時にインポートされている場合は、先にその他のパッケージがインポートされ、その後このパッケージの中にあるパッケージクラス定数と変数が初期化されます。次にinit関数が(もしあれば)実行され、最後に`main`関数が実行されます。以下の図で実行過程を詳しくご説明しています。 + +![](images/2.3.init.png?raw=true) + +図2.6 main関数によるパッケージのインポートと初期化過程の図 + +### import +Goのコードを書いている時は、importコマンドによってパッケージファイルをインポートすることがよくあります。私達が通常使う方法は以下を参考にしてください: + + import( + "fmt" + ) + +その後コードの中では以下のような方法でコールすることができます。 + + fmt.Println("hello world") + +上のfmtはGo言語の標準ライブラリです。実はgorootの下にこのモジュールが加えられています。当然Goのインポートは以下のような2つの方法で自分の書いたモジュールを追加することができます: + +1. 相対パス + + import "./model" //カレントファイルと同じディレクトリにあるmodelディレクトリ、ただし、この方法によるimportはおすすめしません。 + +2. 絶対パス + + import "shorturl/model" //gopath/src/shorturl/modelモジュールを追加します。 + + +ここではimportの通常のいくつかの方法をご説明しました。ただ他にも特殊なimportがあります。新人を悩ませる方法ですが、ここでは一つ一つ一体何がどうなっているのかご説明しましょう + + +1. ドット操作 + + 時々、以下のようなパッケージのインポート方法を見ることがあります + + import( + . "fmt" + ) + + このドット操作の意味はこのパッケージがインポートされた後このパッケージの関数をコールする際、パッケージ名を省略することができます。つまり、前であなたがコールしたようなfmt.Println("hello world")はPrintln("hello world")というように省略することができます。 + +2. エイリアス操作 + + エイリアス操作はその名の通りパッケージ名に他の覚えやすい名前をつけることができます。 + + import( + f "fmt" + ) + + エイリアス操作の場合パッケージ関数をコールする際プレフィックスが自分たちのものになります。すなわち、f.Println("hello world") + +3. _操作 + + この操作は通常とても理解しづらい方法です。以下のimportをご覧ください。 + + import ( + "database/sql" + _ "github.com/ziutek/mymysql/godrv" + ) + + _操作はこのパッケージをインポートするだけでパッケージの中の関数を直接使うわけではなく、このパッケージの中にあるinit関数をコールします。 + + +## links + * [目次]() + * 前へ: [Goの基礎](<02.2.md>) + * 次へ: [struct型](<02.4.md>) diff --git a/ja/ebook/02.8.md b/ja/ebook/02.8.md index 751ff0cb..aa48a728 100644 --- a/ja/ebook/02.8.md +++ b/ja/ebook/02.8.md @@ -1,31 +1,31 @@ -# 2.8 概要 - -この章では主にGo言語のいくつかの文法をご紹介しました。文法を通してGoがいかに簡単かご覧いただけたかと思います。たった25個のキーワードです。もう一度これらキーワードが何に使われるのか見てみることにしましょう。 - - break default func interface select - case defer go map struct - chan else goto package switch - const fallthrough if range type - continue for import return var - -- varとconstは2.2のGo言語の基礎に出てくる変数と定数の宣言を参考にしてください。 -- packageとimportにはすでに少し触れました。 -- func は関数とメソッドの定義に用いられます。 -- return は関数から返るために用いられます。 -- defer はデストラクタのようなものです。 -- go はマルチスレッドに用いられます。 -- select は異なる型の通信を選択するために用いられます。 -- interface はインターフェースを定義するために用いられます。2.6章をご参考ください。 -- struct は抽象データ型の定義に用いられます。2.5章をご参考ください。 -- break、case、continue、for、fallthrough、else、if、switch、goto、defaultは2.3のフロー紹介をご参考ください。 -- chanはchannel通信に用いられます。 -- typeはカスタム定義型の宣言に用いられます。 -- mapはmap型のデータの宣言に用いられます。 -- rangeはslice、map、channelデータの読み込みに用いられます。 - -この25個のキーワードを覚えれば、Goは既に殆ど学び終わったも同然です。 - -## links - * [目次]() - * 前へ: [マルチスレッド](<02.7.md>) - * 次へ: [Webの基礎](<03.0.md>) +# 2.8 概要 + +この章では主にGo言語のいくつかの文法をご紹介しました。文法を通してGoがいかに簡単かご覧いただけたかと思います。たった25個のキーワードです。もう一度これらキーワードが何に使われるのか見てみることにしましょう。 + + break default func interface select + case defer go map struct + chan else goto package switch + const fallthrough if range type + continue for import return var + +- varとconstは2.2のGo言語の基礎に出てくる変数と定数の宣言を参考にしてください。 +- packageとimportにはすでに少し触れました。 +- func は関数とメソッドの定義に用いられます。 +- return は関数から返るために用いられます。 +- defer はデストラクタのようなものです。 +- go はマルチスレッドに用いられます。 +- select は異なる型の通信を選択するために用いられます。 +- interface はインターフェースを定義するために用いられます。2.6章をご参考ください。 +- struct は抽象データ型の定義に用いられます。2.5章をご参考ください。 +- break、case、continue、for、fallthrough、else、if、switch、goto、defaultは2.3のフロー紹介をご参考ください。 +- chanはchannel通信に用いられます。 +- typeはカスタム定義型の宣言に用いられます。 +- mapはmap型のデータの宣言に用いられます。 +- rangeはslice、map、channelデータの読み込みに用いられます。 + +この25個のキーワードを覚えれば、Goは既に殆ど学び終わったも同然です。 + +## links + * [目次]() + * 前へ: [マルチスレッド](<02.7.md>) + * 次へ: [Webの基礎](<03.0.md>) diff --git a/ja/ebook/03.0.md b/ja/ebook/03.0.md index da5e89b6..bf0afd5b 100644 --- a/ja/ebook/03.0.md +++ b/ja/ebook/03.0.md @@ -1,11 +1,11 @@ -# 3 Webの基礎 - -Webのプログラミングの基礎を勉強することはあなたにとってまさにこの本を読む理由でしょう。事実、どのようにGoを使ってWebアプリケーションをプログラムするかはこの本の目的でもあります。前回までで、Goは既に成熟したHttp処理パッケージを持つことをご紹介しました。これによってどのような事情の動的なWebプログラミングも簡単に作成できます。これ以降の章でご紹介する内容はどれもWebプログラミングの範疇です。この章ではWebに関する概念とGo言語がいかにWebプログラムを実行するかに集中して討論します。 - -## 目次 -![](images/navi3.png?raw=true) - -## links - * [目次]() - * 前へ: [第二章概要](<02.8.md>) - * 次へ: [webでの作業方法](<03.1.md>) +# 3 Webの基礎 + +Webのプログラミングの基礎を勉強することはあなたにとってまさにこの本を読む理由でしょう。事実、どのようにGoを使ってWebアプリケーションをプログラムするかはこの本の目的でもあります。前回までで、Goは既に成熟したHttp処理パッケージを持つことをご紹介しました。これによってどのような事情の動的なWebプログラミングも簡単に作成できます。これ以降の章でご紹介する内容はどれもWebプログラミングの範疇です。この章ではWebに関する概念とGo言語がいかにWebプログラムを実行するかに集中して討論します。 + +## 目次 +![](images/navi3.png?raw=true) + +## links + * [目次]() + * 前へ: [第二章概要](<02.8.md>) + * 次へ: [webでの作業方法](<03.1.md>) diff --git a/ja/ebook/03.1.md b/ja/ebook/03.1.md index 754d734c..6d4c9b68 100644 --- a/ja/ebook/03.1.md +++ b/ja/ebook/03.1.md @@ -1,159 +1,159 @@ -# 3.1 Webでの作業方法 - -普段ホームページを閲覧する際、ブラウザを開くと思います。アドレスを入力してエンターキーを押すと、あなたが見たいコンテンツが表示されます。この見た目には簡単なユーザの行動には一体何が隠されているのでしょうか? - -普通のネットワーク上の操作に対して、システムは実はこのように行なっています:ブラウザそのものはクライアントです。URLを入力する際まずブラウザはDNSサーバにアクセスします。DNSを通してドメインと対応するIPを取得し、IPアドレスからIPに対応したサーバを探しだした後、TCPコネクションの設立を要求します。ブラウザがHTTP Request(リクエスト)パケットを送信し終わると、サーバはリクエストパケットを受け取ってリクエストパケットを処理しはじめます。サーバは自分のサービスをコールし、HTTP Response(レスポンス)パケットを返します。クライアントがサーバからのレスポンスを受け取ると、このレスポンスパケットのbodyを読み出します。すべての内容を受け取ると、このサーバとのTCP接続を切断します。 - -![](images/3.1.web2.png?raw=true) - -図3.1 ユーザがWebサーバにアクセスする過程 - -WebサーバはHTTPサーバとも呼ばれます。HTTPプロトコルを通じてクライアントと通信を行います。このクライアントは普通はWebブラウザを指します(実はモバイルクライアントでも内部ではブラウザによって実現されています。) - -Webサーバの動作原理は簡単に説明できます: - -- クライアントがTCP/IPプロトコルによってサーバまでTCP接続を設立します。 -- クライアントはサーバに対してHTTPプロトコルのリクエストパケットを送信し、サーバのリソースドキュメントを要求します。 -- サーバはクライアントに対してHTTPプロトコルの応答パケットを送信し、もし要求されたリソースに動的な言語によるコンテンツが含まれている場合、サーバが動的言語のインタープリターエンジンに"動的な内容"の処理をコールさせます。処理によって得られたデータをクライアントに返します。 -- クライアントとサーバが切断されます。クライアントはHTMLドキュメントを解釈し、クライアントの画面上に図形として結果を表示します。 - -簡単なHTTPタスクはこのように実現されます。見た目にはとても複雑ですが、原理はとても簡単です。気をつけなければならないのは、クライアントとサーバの間の通信は常に接続されているわけではありません。サーバが応答を送信した後クライアントと接続が切断され、次のリクエストを待ち受けます。 - -## URLとDNS解決 -ホームページの閲覧は常にURLの訪問で行われます。ではURLとは一体どういうものなのでしょうか? - -URL(Uniform Resource Locator)は"統一資源位置指定子"の英語の短縮です。ネットワーク上のリソースを表現しています。基本的なシンタックスは以下のとおりです。 - - scheme://host[:port#]/path/.../[?query-string][#anchor] - scheme 低レイヤーで使用されるプロトコルを指定します。(例えば:http, https, ftp) - host HTTPサーバのIPアドレスまたはドメイン - port# HTTPサーバのデフォルトのポート番号は80です。この場合ポート番号は省略することができます。もし別のポートを使用する場合は指定しなければなりません。例えば http://www.cnblogs.com:8080/ - path リソースまでのパス - query-string httpサーバへ送るデータ - anchor アンカー - - DNS(Domain Name System)は"ドメインシステム"の英文の省略です。これは組織の木構造の計算機とネットワークサービスの命名システムです。これはTCP/IPネットワークで使用されます。ホスト名またはドメインを実際のIPアドレスに変換する作業を行う役目を担っています。DNSはこのような翻訳家です。この基本的な動作原理は下の図に示しているとおりです。 - -![](images/3.1.dns_hierachy.png?raw=true) - -図3.2 DNSの動作原理 - -より詳細なDNS解決のプロセスは下のようなものです。このプロセスは我々がDNSの作業モードを理解するのに助けとなります。 - -1. ブラウザでwww.qq.comドメインを入力します。オペレーティングシステムはまず自分のローカルのhostsファイルにこのアドレスがないか検査します。もしあれば、このIPアドレスの設定が適用されます。ドメイン解決終了。 - -2. もしhostsにこのドメインの設定がなければ、ローカルのDNSリゾルバのバッファを探します。もしあれば、これを返します。ドメイン解決終了。 - -3. もしhostsとローカルのDNSリゾルバのバッファのどちらにも目的のドメインがなかった場合、まずTCP/IPのオプションで設定されているプライマリDNSサーバを探します。ここではこれをローカルDNSサーバと呼びましょう。このサーバが要求を受けた時、もし要求したドメイン名がローカルで設定されたリソースの中に含まれている場合、解決の結果をクライアントに返します。ドメイン解決終了。これは権威ある解決です。 - -4. もし要求したドメイン名がローカルDNSサーバのゾーンでは解決できなかったものの、このサーバがこのURLをバッファリングしていた場合このIPアドレスが適用されます。ドメイン名解決終了。これは権威ある解決ではありません。 - -5. もしローカルDNSサーバがそのゾーンファイルとバッファリングのどちらも有効でなかった場合、ローカルDNSサーバの設定に従って(リピータが設定されているか)検査を行います。もし転送モードが使用されていなければローカルDNSはリクエストを"ルートDNSサーバ"に送ります。"ルートDNSサーバ"はリクエストを受け取った後このドメイン名(.com)が誰によって権限を受け管理されているか判断し、このトップレベルドメインの権威サーバのIPを返します。ローカルDNSサーバがIP情報を受け取った後、.comドメインを担当するこのサーバと接続を行います。.comドメインを担当するサーバがリクエストを受け取った後、もし自分で名前解決できなければ、.comドメインを管理するもう一つ下のレイヤーのDNSサーバのアドレス(qq.com)をローカルDNSサーバに送ります。ローカルDNSサーバがこのアドレスを受け取った後、qq.comドメインのサーバを探し出し、www.qq.comのホストが見つかるまで上の動作を繰り返します。 - -6. もし転送モードを使用していれば、このDNSサーバはリクエストをひとつ上のレイヤーのDNSサーバに転送します。このサーバが名前解決を行い、名前が解決できなかった場合は、ルートDNSを探すか、もう一つ上のレイヤーのにリクエストを転送します。またはルートが提示されます。最後に結果をローカルDNSサーバに返し、このDNSサーバはクライアントに返します。 - -![](images/3.1.dns_inquery.png?raw=true) - -図3.3 DNS解決の全体のプロセス - -> いわゆる`再帰検索プロセス`は"検索者"の交代を意味します。また、`反復する検索プロセス`では"検索者"は不変です。 -> -> 例をあげて説明しましょう。あなたは一緒に法律の授業を受けている女の子の電話番号を知りたいとします。あなたはこっそり彼女の写真も撮っています。寝室にもどって、正義感の強いアニキたちにそのことを伝えます。このアニキたちは異議を唱えることもなく、胸を叩いてあなたにこう言います。「急ぐ必要はない。私があなたに替わって調べてあげましょう」(この時一時再帰検索が完了しています。すなわち、検索者の役割が変更されました。)。その後彼は写真を携え学部の4年生の先輩のところに聞きにいきます。「この女の子はxx学部なんですけど・・・」その後このアニキは矢継ぎ早にxx学部のオフィス主任の助手を務めているクラスメートに聞きます。このクラスメートはxx学部のyyゼミであると言います。またこの正義感の強いアニキたちはxx学部のyyゼミのゼミ長のところにいき、この女の子の電話番号をゲットします。(ここまでで何回かの連続検索が完了しました。すなわち、検索者の役は変わっていませんが、聞きに行く対象を反復して取り替えています。)最後に彼は番号をあなたの手に渡すことで全体の検索が完了します。 - -上のステップを通して、IPアドレスを最後に取得します。つまりブラウザが最後にリクエストを送る時はIPにもとづいて、サーバと情報のやりとりをするのです。 - -## HTTPプロトコル詳細 - -HTTPプロトコルはWeb作業の確信です。そのためWebの作業方法をくまなく理解するためには、HTTPがいったいどのような作業を行なっているのか深く理解する必要があります。 - -HTTPはWebサーバにブラウザ(クライアント)とInternetを通してデータをやり取りさせるプロトコルです。これはTCPプロトコルの上で成立しますので、一般的にはTCPの80番ポートが採用されます。これはリクエストとレスポンスのプロトコルです--クライアントはリクエストを送信しサーバがこのリクエストに対してレスポンスを行います。HTTPでは、クライアントは常に接続を行いHTTPリクエストを送信することでタスクをこなします。サーバは主導的にクライアントと接続することはできません。また、クライアントに対してコールバック接続を送信することもできません。クライアントとサーバは事前に接続を中断することができます。例えば、ブラウザでファイルをダウンロードする際、"停止"ボタンをクリックすることでファイルのダウンロードを中断し、サーバとのHTTP接続を閉じることができます。 - -HTTPプロトコルはステートレスです。同じクライアントの前のリクエストと今回のリクエストの間にはなんの対応関係もありません。HTTPサーバからすれば、この2つのリクエストが同じクライアントから発せられたものかすらも知りません。この問題を解決するため、WebプログラムではCookie機構を導入することで、接続の持続可能状態を維持しています。 - ->HTTPプロトコルはTCPプロトコルの上で確立しますので、TCPアタックはHTTPの通信に同じように影響を与えます。例えばよく見かける攻撃として:SYN Floodは現在最も流行したDoS(サービス不能攻撃)とDdoS(分散型サービス不能攻撃)などがあります。これはTCPプロトコルの欠陥を利用して大量に偽造されたTCP接続要求を送信するのです。これにより攻撃された側はリソースが枯渇(CPUの高負荷やメモリ不足)する攻撃です。 - -### HTTPリクエストパケット(ブラウザ情報) - -まずRequestパケットの構造を見てみることにしましょう。Requestパケットは3つの部分にわけられます。第一部分はRequest line(リクエスト行)。第二部分はRequest header(リクエストヘッダ)、第三部分はbody(ボディ)と呼ばれます。headerとbodyの間には空行があり、リクエストパケットの例は以下のようなものです。 - - GET /domains/example/ HTTP/1.1 //リクエスト業:リクエスト方法 リクエストRUI HTTPプロトコル/プロトコルバージョン - Host:www.iana.org //サーバのホスト名 - User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //ブラウザ情報 - Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //クライアントが受け取れるmime - Accept-Encoding:gzip,deflate,sdch //ストリーム圧縮をサポートするか否か - Accept-Charset:UTF-8,*;q=0.5 //クライアントの文字コードセット - //空行、リクエストヘッダとボディを分けるために使われます。 - //ボディ、リソースへのリクエストのオプション、例えばPOSTが渡すオプション - -fiddlerパケットキャプチャを通して下のようなリクエスト情報を見ることができます。 - -![](images/3.1.http.png?raw=true) - -図3.4 fiddlerがキャプチャしたGET情報 - -![](images/3.1.httpPOST.png?raw=true) - -図3.5 fiddlerがキャプチャしたPOST情報 - -**GETリクエストのボディが空であることがわかります。POSTリクエストにはボディがあります**。 - -HTTPプロトコルはサーバに対して交互にリクエストを送る方法が定義されています。基本は四種類。GET,POST,PUT,DELETEです。ひとつのURLアドレスはひとつのネットワーク上のリソースを描写しています。またHTTPの中のGET, POST, PUT, DELETEはこのリソースの検索、修正、増加、削除の4つの操作に対応しています。よく見かけるのはGETとPOSTです。GETは一般的にリソースの情報を取得/検索するために用いられ、POSTはリソース情報を更新するために用いられます。 -GETとPOSTの区別を見てみましょう。 -1. GETが入力するデータはURLの後に置かれます。?によってURLと渡すデータを分割します。オプションの間は&で繋ぎます。例えばEditPosts.aspx?name=test1&id=12345。POSTメソッドは入力するデータをHTTPパケットのBodyの中に置きます。 -2. GETが入力するデータの大きさには制限があります。(ブラウザのURLに対する長に制限があるためです。)またPOSTメソッドで入力するデータには制限がありません。 -3. GETメソッドで入力されたデータはセキュリティの問題を引き起こします。例えばログイン画面があったとして、GETメソッドでデータを入力した場合、ユーザ名とパスワードはURL上にあらわれてしまうことになります。もしページがバッファリングされていたり他の人によっがこのマシンにアクセスすることができれば、ヒストリログからこのユーザのアカウントとパスワードを取得することができてしまいます。 - -### HTTPレスポンスパケット(サーバ情報) -HTTPのresponseパケットを見てみることにしましょう。構造は以下のとおりです: - - HTTP/1.1 200 OK //ステータス行 - Server: nginx/1.0.8 //サーバが使用するWEBソフトウェアの名称及びバージョン - Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //送信時刻 - Content-Type: text/html //サーバが送信するデータの型 - Transfer-Encoding: chunked //送信するHTTPパケットが分解されることを表しています。 - Connection: keep-alive //コネクション状態の保持 - Content-Length: 90 //ボディの内容の長さ - //空行 ヘッダとボディを分けるために使われます。 - ホームページの改良では、HTTPのリクエスト回数を減らすことがあります。つまり、なるべく多くのcssとjsリソースを同じところに集めるのです。目的は出来る限りホームページの静的リソースのリクエスト回数を減少させる事にあります。ホームページの表示速度を上げると同時にサーバのバッファリングを減らす事ができます。 - -## links - * [目次]() - * 前へ: [Webの基礎](<03.0.md>) - * 次へ: [GOでwebサーバを建てる](<03.2.md>) +# 3.1 Webでの作業方法 + +普段ホームページを閲覧する際、ブラウザを開くと思います。アドレスを入力してエンターキーを押すと、あなたが見たいコンテンツが表示されます。この見た目には簡単なユーザの行動には一体何が隠されているのでしょうか? + +普通のネットワーク上の操作に対して、システムは実はこのように行なっています:ブラウザそのものはクライアントです。URLを入力する際まずブラウザはDNSサーバにアクセスします。DNSを通してドメインと対応するIPを取得し、IPアドレスからIPに対応したサーバを探しだした後、TCPコネクションの設立を要求します。ブラウザがHTTP Request(リクエスト)パケットを送信し終わると、サーバはリクエストパケットを受け取ってリクエストパケットを処理しはじめます。サーバは自分のサービスをコールし、HTTP Response(レスポンス)パケットを返します。クライアントがサーバからのレスポンスを受け取ると、このレスポンスパケットのbodyを読み出します。すべての内容を受け取ると、このサーバとのTCP接続を切断します。 + +![](images/3.1.web2.png?raw=true) + +図3.1 ユーザがWebサーバにアクセスする過程 + +WebサーバはHTTPサーバとも呼ばれます。HTTPプロトコルを通じてクライアントと通信を行います。このクライアントは普通はWebブラウザを指します(実はモバイルクライアントでも内部ではブラウザによって実現されています。) + +Webサーバの動作原理は簡単に説明できます: + +- クライアントがTCP/IPプロトコルによってサーバまでTCP接続を設立します。 +- クライアントはサーバに対してHTTPプロトコルのリクエストパケットを送信し、サーバのリソースドキュメントを要求します。 +- サーバはクライアントに対してHTTPプロトコルの応答パケットを送信し、もし要求されたリソースに動的な言語によるコンテンツが含まれている場合、サーバが動的言語のインタープリターエンジンに"動的な内容"の処理をコールさせます。処理によって得られたデータをクライアントに返します。 +- クライアントとサーバが切断されます。クライアントはHTMLドキュメントを解釈し、クライアントの画面上に図形として結果を表示します。 + +簡単なHTTPタスクはこのように実現されます。見た目にはとても複雑ですが、原理はとても簡単です。気をつけなければならないのは、クライアントとサーバの間の通信は常に接続されているわけではありません。サーバが応答を送信した後クライアントと接続が切断され、次のリクエストを待ち受けます。 + +## URLとDNS解決 +ホームページの閲覧は常にURLの訪問で行われます。ではURLとは一体どういうものなのでしょうか? + +URL(Uniform Resource Locator)は"統一資源位置指定子"の英語の短縮です。ネットワーク上のリソースを表現しています。基本的なシンタックスは以下のとおりです。 + + scheme://host[:port#]/path/.../[?query-string][#anchor] + scheme 低レイヤーで使用されるプロトコルを指定します。(例えば:http, https, ftp) + host HTTPサーバのIPアドレスまたはドメイン + port# HTTPサーバのデフォルトのポート番号は80です。この場合ポート番号は省略することができます。もし別のポートを使用する場合は指定しなければなりません。例えば http://www.cnblogs.com:8080/ + path リソースまでのパス + query-string httpサーバへ送るデータ + anchor アンカー + + DNS(Domain Name System)は"ドメインシステム"の英文の省略です。これは組織の木構造の計算機とネットワークサービスの命名システムです。これはTCP/IPネットワークで使用されます。ホスト名またはドメインを実際のIPアドレスに変換する作業を行う役目を担っています。DNSはこのような翻訳家です。この基本的な動作原理は下の図に示しているとおりです。 + +![](images/3.1.dns_hierachy.png?raw=true) + +図3.2 DNSの動作原理 + +より詳細なDNS解決のプロセスは下のようなものです。このプロセスは我々がDNSの作業モードを理解するのに助けとなります。 + +1. ブラウザでwww.qq.comドメインを入力します。オペレーティングシステムはまず自分のローカルのhostsファイルにこのアドレスがないか検査します。もしあれば、このIPアドレスの設定が適用されます。ドメイン解決終了。 + +2. もしhostsにこのドメインの設定がなければ、ローカルのDNSリゾルバのバッファを探します。もしあれば、これを返します。ドメイン解決終了。 + +3. もしhostsとローカルのDNSリゾルバのバッファのどちらにも目的のドメインがなかった場合、まずTCP/IPのオプションで設定されているプライマリDNSサーバを探します。ここではこれをローカルDNSサーバと呼びましょう。このサーバが要求を受けた時、もし要求したドメイン名がローカルで設定されたリソースの中に含まれている場合、解決の結果をクライアントに返します。ドメイン解決終了。これは権威ある解決です。 + +4. もし要求したドメイン名がローカルDNSサーバのゾーンでは解決できなかったものの、このサーバがこのURLをバッファリングしていた場合このIPアドレスが適用されます。ドメイン名解決終了。これは権威ある解決ではありません。 + +5. もしローカルDNSサーバがそのゾーンファイルとバッファリングのどちらも有効でなかった場合、ローカルDNSサーバの設定に従って(リピータが設定されているか)検査を行います。もし転送モードが使用されていなければローカルDNSはリクエストを"ルートDNSサーバ"に送ります。"ルートDNSサーバ"はリクエストを受け取った後このドメイン名(.com)が誰によって権限を受け管理されているか判断し、このトップレベルドメインの権威サーバのIPを返します。ローカルDNSサーバがIP情報を受け取った後、.comドメインを担当するこのサーバと接続を行います。.comドメインを担当するサーバがリクエストを受け取った後、もし自分で名前解決できなければ、.comドメインを管理するもう一つ下のレイヤーのDNSサーバのアドレス(qq.com)をローカルDNSサーバに送ります。ローカルDNSサーバがこのアドレスを受け取った後、qq.comドメインのサーバを探し出し、www.qq.comのホストが見つかるまで上の動作を繰り返します。 + +6. もし転送モードを使用していれば、このDNSサーバはリクエストをひとつ上のレイヤーのDNSサーバに転送します。このサーバが名前解決を行い、名前が解決できなかった場合は、ルートDNSを探すか、もう一つ上のレイヤーのにリクエストを転送します。またはルートが提示されます。最後に結果をローカルDNSサーバに返し、このDNSサーバはクライアントに返します。 + +![](images/3.1.dns_inquery.png?raw=true) + +図3.3 DNS解決の全体のプロセス + +> いわゆる`再帰検索プロセス`は"検索者"の交代を意味します。また、`反復する検索プロセス`では"検索者"は不変です。 +> +> 例をあげて説明しましょう。あなたは一緒に法律の授業を受けている女の子の電話番号を知りたいとします。あなたはこっそり彼女の写真も撮っています。寝室にもどって、正義感の強いアニキたちにそのことを伝えます。このアニキたちは異議を唱えることもなく、胸を叩いてあなたにこう言います。「急ぐ必要はない。私があなたに替わって調べてあげましょう」(この時一時再帰検索が完了しています。すなわち、検索者の役割が変更されました。)。その後彼は写真を携え学部の4年生の先輩のところに聞きにいきます。「この女の子はxx学部なんですけど・・・」その後このアニキは矢継ぎ早にxx学部のオフィス主任の助手を務めているクラスメートに聞きます。このクラスメートはxx学部のyyゼミであると言います。またこの正義感の強いアニキたちはxx学部のyyゼミのゼミ長のところにいき、この女の子の電話番号をゲットします。(ここまでで何回かの連続検索が完了しました。すなわち、検索者の役は変わっていませんが、聞きに行く対象を反復して取り替えています。)最後に彼は番号をあなたの手に渡すことで全体の検索が完了します。 + +上のステップを通して、IPアドレスを最後に取得します。つまりブラウザが最後にリクエストを送る時はIPにもとづいて、サーバと情報のやりとりをするのです。 + +## HTTPプロトコル詳細 + +HTTPプロトコルはWeb作業の確信です。そのためWebの作業方法をくまなく理解するためには、HTTPがいったいどのような作業を行なっているのか深く理解する必要があります。 + +HTTPはWebサーバにブラウザ(クライアント)とInternetを通してデータをやり取りさせるプロトコルです。これはTCPプロトコルの上で成立しますので、一般的にはTCPの80番ポートが採用されます。これはリクエストとレスポンスのプロトコルです--クライアントはリクエストを送信しサーバがこのリクエストに対してレスポンスを行います。HTTPでは、クライアントは常に接続を行いHTTPリクエストを送信することでタスクをこなします。サーバは主導的にクライアントと接続することはできません。また、クライアントに対してコールバック接続を送信することもできません。クライアントとサーバは事前に接続を中断することができます。例えば、ブラウザでファイルをダウンロードする際、"停止"ボタンをクリックすることでファイルのダウンロードを中断し、サーバとのHTTP接続を閉じることができます。 + +HTTPプロトコルはステートレスです。同じクライアントの前のリクエストと今回のリクエストの間にはなんの対応関係もありません。HTTPサーバからすれば、この2つのリクエストが同じクライアントから発せられたものかすらも知りません。この問題を解決するため、WebプログラムではCookie機構を導入することで、接続の持続可能状態を維持しています。 + +>HTTPプロトコルはTCPプロトコルの上で確立しますので、TCPアタックはHTTPの通信に同じように影響を与えます。例えばよく見かける攻撃として:SYN Floodは現在最も流行したDoS(サービス不能攻撃)とDdoS(分散型サービス不能攻撃)などがあります。これはTCPプロトコルの欠陥を利用して大量に偽造されたTCP接続要求を送信するのです。これにより攻撃された側はリソースが枯渇(CPUの高負荷やメモリ不足)する攻撃です。 + +### HTTPリクエストパケット(ブラウザ情報) + +まずRequestパケットの構造を見てみることにしましょう。Requestパケットは3つの部分にわけられます。第一部分はRequest line(リクエスト行)。第二部分はRequest header(リクエストヘッダ)、第三部分はbody(ボディ)と呼ばれます。headerとbodyの間には空行があり、リクエストパケットの例は以下のようなものです。 + + GET /domains/example/ HTTP/1.1 //リクエスト業:リクエスト方法 リクエストRUI HTTPプロトコル/プロトコルバージョン + Host:www.iana.org //サーバのホスト名 + User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //ブラウザ情報 + Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //クライアントが受け取れるmime + Accept-Encoding:gzip,deflate,sdch //ストリーム圧縮をサポートするか否か + Accept-Charset:UTF-8,*;q=0.5 //クライアントの文字コードセット + //空行、リクエストヘッダとボディを分けるために使われます。 + //ボディ、リソースへのリクエストのオプション、例えばPOSTが渡すオプション + +fiddlerパケットキャプチャを通して下のようなリクエスト情報を見ることができます。 + +![](images/3.1.http.png?raw=true) + +図3.4 fiddlerがキャプチャしたGET情報 + +![](images/3.1.httpPOST.png?raw=true) + +図3.5 fiddlerがキャプチャしたPOST情報 + +**GETリクエストのボディが空であることがわかります。POSTリクエストにはボディがあります**。 + +HTTPプロトコルはサーバに対して交互にリクエストを送る方法が定義されています。基本は四種類。GET,POST,PUT,DELETEです。ひとつのURLアドレスはひとつのネットワーク上のリソースを描写しています。またHTTPの中のGET, POST, PUT, DELETEはこのリソースの検索、修正、増加、削除の4つの操作に対応しています。よく見かけるのはGETとPOSTです。GETは一般的にリソースの情報を取得/検索するために用いられ、POSTはリソース情報を更新するために用いられます。 +GETとPOSTの区別を見てみましょう。 +1. GETが入力するデータはURLの後に置かれます。?によってURLと渡すデータを分割します。オプションの間は&で繋ぎます。例えばEditPosts.aspx?name=test1&id=12345。POSTメソッドは入力するデータをHTTPパケットのBodyの中に置きます。 +2. GETが入力するデータの大きさには制限があります。(ブラウザのURLに対する長に制限があるためです。)またPOSTメソッドで入力するデータには制限がありません。 +3. GETメソッドで入力されたデータはセキュリティの問題を引き起こします。例えばログイン画面があったとして、GETメソッドでデータを入力した場合、ユーザ名とパスワードはURL上にあらわれてしまうことになります。もしページがバッファリングされていたり他の人によっがこのマシンにアクセスすることができれば、ヒストリログからこのユーザのアカウントとパスワードを取得することができてしまいます。 + +### HTTPレスポンスパケット(サーバ情報) +HTTPのresponseパケットを見てみることにしましょう。構造は以下のとおりです: + + HTTP/1.1 200 OK //ステータス行 + Server: nginx/1.0.8 //サーバが使用するWEBソフトウェアの名称及びバージョン + Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //送信時刻 + Content-Type: text/html //サーバが送信するデータの型 + Transfer-Encoding: chunked //送信するHTTPパケットが分解されることを表しています。 + Connection: keep-alive //コネクション状態の保持 + Content-Length: 90 //ボディの内容の長さ + //空行 ヘッダとボディを分けるために使われます。 + ホームページの改良では、HTTPのリクエスト回数を減らすことがあります。つまり、なるべく多くのcssとjsリソースを同じところに集めるのです。目的は出来る限りホームページの静的リソースのリクエスト回数を減少させる事にあります。ホームページの表示速度を上げると同時にサーバのバッファリングを減らす事ができます。 + +## links + * [目次]() + * 前へ: [Webの基礎](<03.0.md>) + * 次へ: [GOでwebサーバを建てる](<03.2.md>) diff --git a/ja/ebook/03.2.md b/ja/ebook/03.2.md index 67242ad8..a8e8565c 100644 --- a/ja/ebook/03.2.md +++ b/ja/ebook/03.2.md @@ -1,66 +1,66 @@ -# 3.2 GOで簡単なwebサーバを立てる - -前の節でWebはHTTPプロトコルに基づいたサービスであるとご紹介しました。Go言語では完全なnet/httpパッケージを提供しています。httpパッケージを通して実行できるwebサービスを非常に簡単に立ち上げる事ができます。同時にこのパッケージを使用することで、簡単にwebのルーティング、静的なファイル、テンプレート、cookie等のデータに対して設定と操作を行うことができます。 - -## httpパッケージでwebサーバを立てる - - package main - - import ( - "fmt" - "net/http" - "strings" - "log" - ) - - func sayhelloName(w http.ResponseWriter, r *http.Request) { - r.ParseForm() //オプションを解析します。デフォルトでは解析しません。 - fmt.Println(r.Form) //このデータはサーバのプリント情報に出力されます。 - fmt.Println("path", r.URL.Path) - fmt.Println("scheme", r.URL.Scheme) - fmt.Println(r.Form["url_long"]) - for k, v := range r.Form { - fmt.Println("key:", k) - fmt.Println("val:", strings.Join(v, "")) - } - fmt.Fprintf(w, "Hello astaxie!") //ここでwに入るものがクライアントに出力されます。 - } - - func main() { - http.HandleFunc("/", sayhelloName) //アクセスのルーティングを設定します。 - err := http.ListenAndServe(":9090", nil) //監視するポートを設定します。 - if err != nil { - log.Fatal("ListenAndServe: ", err) - } - } - -上のコードはbuildした後、web.exeを実行した際、9090ポートでhttpリンクリクエストを監視します。 - -ブラウザで`http://localhost:9090`を入力してください。 - -ブラウザで`Hello astaxie!`と出力されたのが見えたかと思います。 - -アドレスを変えて試してみましょう:`http://localhost:9090/?url_long=111&url_long=222` - -ブラウザで出力されたものは何でしょうか。サーバは何と出力していますか? - -サーバで出力される情報は以下の通りです: - -![](images/3.2.goweb.png?raw=true) - -図3.8 ユーザがWebにアクセスしてサーバが出力する情報 - -上のコードでwebサーバを書くためにはhttpパッケージの2つの関数を呼ぶだけで良いことがわかります。 - ->もしあなたが以前PHPプログラマであれば。こう問うかもしれません。我々のnginx、apacheサーバは必要ないのですかと?なぜならこいつは直接tcpポートを関ししますので、nginxがsやることをやってくれます。またsayhelloNameは実は我々が書いたロジック関数ですので、phpの中のコントローラ(controller)関数に近いものです。 - ->もしあなたがpythonプログラマであったのなら、tornadoを聞いたことがあると思います。このコードはそれとよく似ていませんか?ええ、その通りです。goはpythonのような動的な言語によく似た特性を持っています。webアプリケーションを書くにはとても便利です。 - ->もしあなたがrubyプログラマであったのなら、RORの/script/serverを起動したのと少し似ている事に気づいたかもしれません。 - -Goを通じて簡単な数行のコードでwebサーバを立ち上げることができました。さらにこのWebサーバの内部ではマルチスレッドの特性をサポートしています。続く2つの節でgoが以下にWebのマルチスレッドを実現しているのか細かくご紹介します。 - -## links - * [目次]() - * 前へ: [Webの作業方法](<03.1.md>) - * 次へ: [Goはどのようにしてweb作業を行うか](<03.3.md>) +# 3.2 GOで簡単なwebサーバを立てる + +前の節でWebはHTTPプロトコルに基づいたサービスであるとご紹介しました。Go言語では完全なnet/httpパッケージを提供しています。httpパッケージを通して実行できるwebサービスを非常に簡単に立ち上げる事ができます。同時にこのパッケージを使用することで、簡単にwebのルーティング、静的なファイル、テンプレート、cookie等のデータに対して設定と操作を行うことができます。 + +## httpパッケージでwebサーバを立てる + + package main + + import ( + "fmt" + "net/http" + "strings" + "log" + ) + + func sayhelloName(w http.ResponseWriter, r *http.Request) { + r.ParseForm() //オプションを解析します。デフォルトでは解析しません。 + fmt.Println(r.Form) //このデータはサーバのプリント情報に出力されます。 + fmt.Println("path", r.URL.Path) + fmt.Println("scheme", r.URL.Scheme) + fmt.Println(r.Form["url_long"]) + for k, v := range r.Form { + fmt.Println("key:", k) + fmt.Println("val:", strings.Join(v, "")) + } + fmt.Fprintf(w, "Hello astaxie!") //ここでwに入るものがクライアントに出力されます。 + } + + func main() { + http.HandleFunc("/", sayhelloName) //アクセスのルーティングを設定します。 + err := http.ListenAndServe(":9090", nil) //監視するポートを設定します。 + if err != nil { + log.Fatal("ListenAndServe: ", err) + } + } + +上のコードはbuildした後、web.exeを実行した際、9090ポートでhttpリンクリクエストを監視します。 + +ブラウザで`http://localhost:9090`を入力してください。 + +ブラウザで`Hello astaxie!`と出力されたのが見えたかと思います。 + +アドレスを変えて試してみましょう:`http://localhost:9090/?url_long=111&url_long=222` + +ブラウザで出力されたものは何でしょうか。サーバは何と出力していますか? + +サーバで出力される情報は以下の通りです: + +![](images/3.2.goweb.png?raw=true) + +図3.8 ユーザがWebにアクセスしてサーバが出力する情報 + +上のコードでwebサーバを書くためにはhttpパッケージの2つの関数を呼ぶだけで良いことがわかります。 + +>もしあなたが以前PHPプログラマであれば。こう問うかもしれません。我々のnginx、apacheサーバは必要ないのですかと?なぜならこいつは直接tcpポートを関ししますので、nginxがsやることをやってくれます。またsayhelloNameは実は我々が書いたロジック関数ですので、phpの中のコントローラ(controller)関数に近いものです。 + +>もしあなたがpythonプログラマであったのなら、tornadoを聞いたことがあると思います。このコードはそれとよく似ていませんか?ええ、その通りです。goはpythonのような動的な言語によく似た特性を持っています。webアプリケーションを書くにはとても便利です。 + +>もしあなたがrubyプログラマであったのなら、RORの/script/serverを起動したのと少し似ている事に気づいたかもしれません。 + +Goを通じて簡単な数行のコードでwebサーバを立ち上げることができました。さらにこのWebサーバの内部ではマルチスレッドの特性をサポートしています。続く2つの節でgoが以下にWebのマルチスレッドを実現しているのか細かくご紹介します。 + +## links + * [目次]() + * 前へ: [Webの作業方法](<03.1.md>) + * 次へ: [Goはどのようにしてweb作業を行うか](<03.3.md>) diff --git a/ja/ebook/03.5.md b/ja/ebook/03.5.md index 6ad41881..94b84f82 100644 --- a/ja/ebook/03.5.md +++ b/ja/ebook/03.5.md @@ -1,9 +1,9 @@ -# 3.5 概要 -この章ではHTTPプロトコル、DNS名前解決のプロセス、どのようにしてgoで簡単なweb serverを実装するかご紹介しました。net/httpパッケージのソースコードに触れるうちにこのserverを実装する秘密についてお分かりいただけたかと思います。 - -この章の学習を通じて、GoによるWeb開発の初歩をご理解いただければ幸いです。我々はまた対応するコードを見ることでGoでWebアプリケーションを開発することがとても便利でまた相当柔軟であると分かりました。 - -## links - * [目次]() - * 前へ: [Goのhttpパッケージ詳細](<03.4.md>) - * 次へ: [フォーム](<04.0.md>) +# 3.5 概要 +この章ではHTTPプロトコル、DNS名前解決のプロセス、どのようにしてgoで簡単なweb serverを実装するかご紹介しました。net/httpパッケージのソースコードに触れるうちにこのserverを実装する秘密についてお分かりいただけたかと思います。 + +この章の学習を通じて、GoによるWeb開発の初歩をご理解いただければ幸いです。我々はまた対応するコードを見ることでGoでWebアプリケーションを開発することがとても便利でまた相当柔軟であると分かりました。 + +## links + * [目次]() + * 前へ: [Goのhttpパッケージ詳細](<03.4.md>) + * 次へ: [フォーム](<04.0.md>) diff --git a/ja/ebook/04.0.md b/ja/ebook/04.0.md index a35947c3..9a887076 100644 --- a/ja/ebook/04.0.md +++ b/ja/ebook/04.0.md @@ -1,25 +1,25 @@ -# 4 フォーム - -フォームは我々が普段Webアプリケーションを書く時によく使うツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると、少々ばかり門外漢かもしれません。フォームとは一体何でしょうか? - -フォームは表の要素を含むエリアです。フォームの要素はユーザがフォームの中で(例えば、テキストフィールド、コンボボックス、チェックボックス、セレクトボックス等です。)情報を入力する要素です。フォームはフォームタグ(\)で定義します。 - -
- ... - input 要素 - ... -
- -Goではformの処理にすでにとても簡単な方法が用意されています。Requestの中にformを専門に処理するものがあります。とても簡単にWeb開発に利用できるものです。4.1節の中でGoがどのようにフォームの入力を処理するかご説明します。いかなるユーザの入力も信用はできないので、これらの入力に対しバリデーションを行う必要があります。4.2節ではどのように普通のバリデーションを行うか、細かいデモンストレーションを行います。 - -HTTPプロトコルはステートレスなプロトコルです。ではどのようにして一人のユーザを同定するのでしょうか?また、フォームが複数回送信されてしまわないように保証するにはどうするのでしょうか?4.3と4.4節ではcookie(cookieはクライアントに保存される情報です。handlerとサーバを通る度にやり取りされるデータです。)等をより詳しくご紹介します。 - -フォームにはもうひとつ、ファイルをアップロードできるという大きな機能があります。Goはファイルのアップロードをどのように処理しているのでしょうか?大きなファイルをアップロードする際効率よく処理するにはどうすればよいでしょうか?4.5節ではGoによるファイルのアップロード処理の知識を一緒に勉強します。 - -## 目次 -![](images/navi4.png?raw=true) - -## links - * [目次]() - * 前へ: [第三章概要](<03.5.md>) - * 次へ: [フォームの入力を処理する](<04.1.md>) +# 4 フォーム + +フォームは我々が普段Webアプリケーションを書く時によく使うツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると、少々ばかり門外漢かもしれません。フォームとは一体何でしょうか? + +フォームは表の要素を含むエリアです。フォームの要素はユーザがフォームの中で(例えば、テキストフィールド、コンボボックス、チェックボックス、セレクトボックス等です。)情報を入力する要素です。フォームはフォームタグ(\)で定義します。 + +
+ ... + input 要素 + ... +
+ +Goではformの処理にすでにとても簡単な方法が用意されています。Requestの中にformを専門に処理するものがあります。とても簡単にWeb開発に利用できるものです。4.1節の中でGoがどのようにフォームの入力を処理するかご説明します。いかなるユーザの入力も信用はできないので、これらの入力に対しバリデーションを行う必要があります。4.2節ではどのように普通のバリデーションを行うか、細かいデモンストレーションを行います。 + +HTTPプロトコルはステートレスなプロトコルです。ではどのようにして一人のユーザを同定するのでしょうか?また、フォームが複数回送信されてしまわないように保証するにはどうするのでしょうか?4.3と4.4節ではcookie(cookieはクライアントに保存される情報です。handlerとサーバを通る度にやり取りされるデータです。)等をより詳しくご紹介します。 + +フォームにはもうひとつ、ファイルをアップロードできるという大きな機能があります。Goはファイルのアップロードをどのように処理しているのでしょうか?大きなファイルをアップロードする際効率よく処理するにはどうすればよいでしょうか?4.5節ではGoによるファイルのアップロード処理の知識を一緒に勉強します。 + +## 目次 +![](images/navi4.png?raw=true) + +## links + * [目次]() + * 前へ: [第三章概要](<03.5.md>) + * 次へ: [フォームの入力を処理する](<04.1.md>) diff --git a/ja/ebook/04.3.md b/ja/ebook/04.3.md index a9231f33..36876a96 100644 --- a/ja/ebook/04.3.md +++ b/ja/ebook/04.3.md @@ -1,68 +1,68 @@ -# 4.3 クロスサイトスクリプティングの予防 - -現在のホームページは大量の動的なコンテンツを含み、ユーザのエクスペリエンスを高めています。以前に比べてとても複雑になっています。いわゆる動的なコンテンツとは、ユーザの環境と要求により、Webアプリケーションが目的の内容を出力できることを指します。動的なホームページは"クロスサイトスクリプティング"(Cross Site Scripting、セキュリティ専門家が一般的にXSSと省略するもの)と呼ばれる攻撃を受けることがあります。 - -攻撃者は通常セキュリティホールのあるプログラム中にJavaScript、VBScript、ActiveXまたはFlashを挿入することでユーザを騙します。一旦攻撃が成功するとユーザアカウント情報が盗まれ、ユーザの設定を改ざんされてしまったり、cookieを盗みまたは汚染して悪意ある広告を埋め込んだりされます。 - -XSSに対する最も効果的な予防は以下の二種類を組み合わせることです:すべての入力データを検証し、攻撃の検査をすること(これに関しては前の節でいくつかご紹介しました)。もうひとつは出力されるデータに対し適切な処理を行うことによって、すでに挿入されてしまったいかなるスクリプトに対してもブラウザで実行されないようにすることです。 - -Goではどのようにこの効果的な防御を行なっているのでしょうか?Goのhtml/templateの中では以下のいくつかの関数によってエスケープすることができます。 - -- func HTMLEscape(w io.Writer, b []byte) //bに対してエスケープを行い、wに出力する。 -- func HTMLEscapeString(s string) string //sに対してエスケープを行い、結果の文字列を返す。 -- func HTMLEscaper(args ...interface{}) string //複数の引数を同時にエスケープします。結果となる文字列を返します。 - - -4.1節の例を見てみましょう。 - - fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバ側に出力されます。 - fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) - template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力されます。 - -もし入力されたusernameが``だった場合、ブラウザ上では以下のように表示されます: - -![](images/4.3.escape.png?raw=true) - -図4.3 Javascriptフィルターによる出力 - -Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。しかし時にはこの``を正常な情報として出力したい場合があるかもしれません。そのような場合はどのように処理するべきでしょうか?この場合はtext/templateをご利用ください。下の例をご覧ください: - - import "text/template" - ... - t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.ExecuteTemplate(out, "T", "") - -出力 - - Hello, ! - -またはtemplate.HTML型を使用すると - - import "html/template" - ... - t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.ExecuteTemplate(out, "T", template.HTML("")) - -出力 - - Hello, ! - -`template.HTML`に変換した後も、変数の内容はエスケープされません。  - -エスケープの例: - - import "html/template" - ... - t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.ExecuteTemplate(out, "T", "") - -エスケープ後の出力: - - Hello, <script>alert('you have been pwned')</script>! - - - -## links - * [目次]() - * 前へ: [入力値の検証](<04.2.md>) - * 次へ: [フォームの複数回送信の防止](<04.4.md>) +# 4.3 クロスサイトスクリプティングの予防 + +現在のホームページは大量の動的なコンテンツを含み、ユーザのエクスペリエンスを高めています。以前に比べてとても複雑になっています。いわゆる動的なコンテンツとは、ユーザの環境と要求により、Webアプリケーションが目的の内容を出力できることを指します。動的なホームページは"クロスサイトスクリプティング"(Cross Site Scripting、セキュリティ専門家が一般的にXSSと省略するもの)と呼ばれる攻撃を受けることがあります。 + +攻撃者は通常セキュリティホールのあるプログラム中にJavaScript、VBScript、ActiveXまたはFlashを挿入することでユーザを騙します。一旦攻撃が成功するとユーザアカウント情報が盗まれ、ユーザの設定を改ざんされてしまったり、cookieを盗みまたは汚染して悪意ある広告を埋め込んだりされます。 + +XSSに対する最も効果的な予防は以下の二種類を組み合わせることです:すべての入力データを検証し、攻撃の検査をすること(これに関しては前の節でいくつかご紹介しました)。もうひとつは出力されるデータに対し適切な処理を行うことによって、すでに挿入されてしまったいかなるスクリプトに対してもブラウザで実行されないようにすることです。 + +Goではどのようにこの効果的な防御を行なっているのでしょうか?Goのhtml/templateの中では以下のいくつかの関数によってエスケープすることができます。 + +- func HTMLEscape(w io.Writer, b []byte) //bに対してエスケープを行い、wに出力する。 +- func HTMLEscapeString(s string) string //sに対してエスケープを行い、結果の文字列を返す。 +- func HTMLEscaper(args ...interface{}) string //複数の引数を同時にエスケープします。結果となる文字列を返します。 + + +4.1節の例を見てみましょう。 + + fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバ側に出力されます。 + fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) + template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力されます。 + +もし入力されたusernameが``だった場合、ブラウザ上では以下のように表示されます: + +![](images/4.3.escape.png?raw=true) + +図4.3 Javascriptフィルターによる出力 + +Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。しかし時にはこの``を正常な情報として出力したい場合があるかもしれません。そのような場合はどのように処理するべきでしょうか?この場合はtext/templateをご利用ください。下の例をご覧ください: + + import "text/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") + +出力 + + Hello, ! + +またはtemplate.HTML型を使用すると + + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", template.HTML("")) + +出力 + + Hello, ! + +`template.HTML`に変換した後も、変数の内容はエスケープされません。  + +エスケープの例: + + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") + +エスケープ後の出力: + + Hello, <script>alert('you have been pwned')</script>! + + + +## links + * [目次]() + * 前へ: [入力値の検証](<04.2.md>) + * 次へ: [フォームの複数回送信の防止](<04.4.md>) diff --git a/ja/ebook/04.4.md b/ja/ebook/04.4.md index 047bca31..e47d9827 100644 --- a/ja/ebook/04.4.md +++ b/ja/ebook/04.4.md @@ -1,58 +1,58 @@ -# 4.4 フォームの複数回送信の防止 - -以前どこかのBBSやブログでご覧になったかもしれませんが、一つのスレや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因で、ユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか? - -解決方法はフォームの中に唯一の値を持ったhiddenフィールドを追加することです。フォームを検証する際、この唯一の値を持ったフォームがすでに送信されているかどうか検証します。もしすでに送信されていれば、二回目の送信を拒絶します。そうでなければフォームに対して処理ロジックを行います。また、もしAjax形式で送信するフォームだった場合、フォームが送信された後、javascriptによってフォームの送信ボタンを禁止します。 - -4.2節の例を改良してみましょう: - - サッカー - バスケットボール - テニス - ユーザ名: - パスワード: - - - -テンプレートの中に`token`というhiddenフィールドを追加しました。この値にはMD5(タイムスタンプ)によってユニークな値を割り当てます。この値をサーバに保存することで(sessionによってコントロールは、6章でどのように保存するか解説します)フォームが送信される際の判定に使うことができます。 - - func login(w http.ResponseWriter, r *http.Request) { - fmt.Println("method:", r.Method) //リクエストを受け取る方法 - if r.Method == "GET" { - crutime := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(crutime, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) - - t, _ := template.ParseFiles("login.gtpl") - t.Execute(w, token) - } else { - //リクエストはログインデータです。ログインのロジックを実行して判断します。 - r.ParseForm() - token := r.Form.Get("token") - if token != "" { - //tokenの合法性を検証します。 - } else { - //tokenが存在しなければエラーを出します。 - } - fmt.Println("username length:", len(r.Form["username"][0])) - fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバ側に出力します。 - fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) - template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力します。 - } - } - -出力されるページのソースは以下の通り: - -![](images/4.4.token.png?raw=true) - -図4.4 tokenを追加した後クライアントが出力するソース情報 - -tokenはすでに出力値を持っていますので、連続してページを更新することができます。この値が次々と変化するのがお分かりいただけるかと思います。このように毎回formが表示される時にユニークになるよう保証します。ユーザが送信するフォームは唯一性が保持されます。 - -この解決方法は悪意の無い攻撃に対しても防止することができます。また悪意のあるユーザに対してもしばらく効果があります。その後、この悪意のある動機を捨て去ることができなかった場合は更に複雑な作業が必要となります。 - -## links - * [目次]() - * 前へ: [クロスサイトスクリプティングの予防](<04.3.md>) - * 次へ: [ファイルのアップロード処理](<04.5.md>) +# 4.4 フォームの複数回送信の防止 + +以前どこかのBBSやブログでご覧になったかもしれませんが、一つのスレや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因で、ユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか? + +解決方法はフォームの中に唯一の値を持ったhiddenフィールドを追加することです。フォームを検証する際、この唯一の値を持ったフォームがすでに送信されているかどうか検証します。もしすでに送信されていれば、二回目の送信を拒絶します。そうでなければフォームに対して処理ロジックを行います。また、もしAjax形式で送信するフォームだった場合、フォームが送信された後、javascriptによってフォームの送信ボタンを禁止します。 + +4.2節の例を改良してみましょう: + + サッカー + バスケットボール + テニス + ユーザ名: + パスワード: + + + +テンプレートの中に`token`というhiddenフィールドを追加しました。この値にはMD5(タイムスタンプ)によってユニークな値を割り当てます。この値をサーバに保存することで(sessionによってコントロールは、6章でどのように保存するか解説します)フォームが送信される際の判定に使うことができます。 + + func login(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) //リクエストを受け取る方法 + if r.Method == "GET" { + crutime := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(crutime, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + t, _ := template.ParseFiles("login.gtpl") + t.Execute(w, token) + } else { + //リクエストはログインデータです。ログインのロジックを実行して判断します。 + r.ParseForm() + token := r.Form.Get("token") + if token != "" { + //tokenの合法性を検証します。 + } else { + //tokenが存在しなければエラーを出します。 + } + fmt.Println("username length:", len(r.Form["username"][0])) + fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバ側に出力します。 + fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) + template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力します。 + } + } + +出力されるページのソースは以下の通り: + +![](images/4.4.token.png?raw=true) + +図4.4 tokenを追加した後クライアントが出力するソース情報 + +tokenはすでに出力値を持っていますので、連続してページを更新することができます。この値が次々と変化するのがお分かりいただけるかと思います。このように毎回formが表示される時にユニークになるよう保証します。ユーザが送信するフォームは唯一性が保持されます。 + +この解決方法は悪意の無い攻撃に対しても防止することができます。また悪意のあるユーザに対してもしばらく効果があります。その後、この悪意のある動機を捨て去ることができなかった場合は更に複雑な作業が必要となります。 + +## links + * [目次]() + * 前へ: [クロスサイトスクリプティングの予防](<04.3.md>) + * 次へ: [ファイルのアップロード処理](<04.5.md>) diff --git a/ja/ebook/04.5.md b/ja/ebook/04.5.md index 7d57b5d9..9e109411 100644 --- a/ja/ebook/04.5.md +++ b/ja/ebook/04.5.md @@ -1,155 +1,155 @@ -# 4.5 ファイルのアップロード処理 -ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか? - -フォームにファイルをアップロードさせるためには、まずformの`enctype`属性を追加する必要があります。`enctype`属性には以下の3つの状態あります: - - application/x-www-form-urlencoded 送信前にすべての文字列をエンコードする(デフォルト) - multipart/form-data 文字列に対してエンコードしません。ファイルのアップロードウィジェットを含むフォームを使用するときはこの値が必要です。 - text/plain 空白を"+"記号に置き換えます。ただし、特殊文字に対してエンコードは行われません。 - -そのため、フォームのhtmlコードはこのようになります: - - - - ファイルアップロード - - -
- - - -
- - - -サーバでは、handlerFuncをひとつ追加します: - - http.HandleFunc("/upload", upload) - - // /uploadを処理するロジック - func upload(w http.ResponseWriter, r *http.Request) { - fmt.Println("method:", r.Method) //リクエストを受け取るメソッド - if r.Method == "GET" { - crutime := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(crutime, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) - - t, _ := template.ParseFiles("upload.gtpl") - t.Execute(w, token) - } else { - r.ParseMultipartForm(32 << 20) - file, handler, err := r.FormFile("uploadfile") - if err != nil { - fmt.Println(err) - return - } - defer file.Close() - fmt.Fprintf(w, "%v", handler.Header) - f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - fmt.Println(err) - return - } - defer f.Close() - io.Copy(f, file) - } - } - -上のコードでは、ファイルのアップロードを処理するためには`r.ParseMultipartForm`をコールする必要があります。引数には`maxMemory`が表示されています。`ParseMultipartForm`をコールした後、アップロードするファイルは`maxMemory`のサイズのメモリに保存されます。もしファイルのサイズが`maxMemory`を超えた場合、残った部分はシステムのテンポラリファイルに保存されます。`r.FormFile`によって上のファイルハンドルを取得することができます。その後実例の中では`io.Copy`を使ってファイルを保存しています。 - ->その他のファイルではないフィールド情報を取得する時は`r.ParseForm`をコールする必要はありません。必要な時はGoが自動でコールします。また`ParseMultipartFrom`を一度コールすると、その後にもう一度コールしても効果はありません。 - -上の実例を通して、ファイルのアップロードには主に3ステップの処理があることが分かります: - -1. フォームにenctype="multipart/form-data"を追加する。 -2. サーバで`r.ParseMultipartForm`をコールし、アップロードするファイルをメモリとテンポラリファイルに保存する。 -3. `r.FormFile`を使用して、ファイルハンドルを取得し、ファイルに対して保存等の処理を行う。 - -ファイルhandlerはmultipart.FileHnadlerです。この中には以下のような構造体が保存されています。 - - type FileHeader struct { - Filename string - Header textproto.MIMEHeader - // contains filtered or unexported fields - } - -上のコード例では以下のようにファイルのアップロードを出力します。 - -![](images/4.5.upload2.png?raw=true) - -図4.5 ファイルのアップロードを行った後サーバが受け取った情報の出力 - -## クライアントによるファイルのアップロード - -上の例でどのようにフォームからファイルをアップロードするのか示しました。その後サーバでファイルを処理しますが、Goは実はクライアントのフォームによるファイルのアップロードをエミュレートする機能をサポートしています。詳しい仕様方法は以下の例をご覧ください: - - package main - - import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net/http" - "os" - ) - - func postFile(filename string, targetUrl string) error { - bodyBuf := &bytes.Buffer{} - bodyWriter := multipart.NewWriter(bodyBuf) - - //キーとなる操作 - fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) - if err != nil { - fmt.Println("error writing to buffer") - return err - } - - //ファイルハンドル操作をオープンする - fh, err := os.Open(filename) - if err != nil { - fmt.Println("error opening file") - return err - } - - //iocopy - _, err = io.Copy(fileWriter, fh) - if err != nil { - return err - } - - contentType := bodyWriter.FormDataContentType() - bodyWriter.Close() - - resp, err := http.Post(targetUrl, contentType, bodyBuf) - if err != nil { - return err - } - defer resp.Body.Close() - resp_body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - fmt.Println(resp.Status) - fmt.Println(string(resp_body)) - return nil - } - - // sample usage - func main() { - target_url := "http://localhost:9090/upload" - filename := "./astaxie.pdf" - postFile(filename, target_url) - } - - -上の例ではクライアントが如何にサーバに対し一つのファイルをアップロードするのかご説明しました。クライアントはmultipart.Writeを通してファイルの本文をバッファの中に書き込みます。その後、httpのPostメソッドをコールしてバッファからサーバに転送します。 - ->もしあなたが他にusernameといった普通のフィールドを同時に書き込む場合は、multipartのWriteFieldメソッドをコールして、その他の似たようなフィールドを複数書き込むことができます。 - -## links - * [目次]() - * 前へ: [フォームの多重送信の防止](<04.4.md>) - * 次へ: [概要](<04.6.md>) +# 4.5 ファイルのアップロード処理 +ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか? + +フォームにファイルをアップロードさせるためには、まずformの`enctype`属性を追加する必要があります。`enctype`属性には以下の3つの状態あります: + + application/x-www-form-urlencoded 送信前にすべての文字列をエンコードする(デフォルト) + multipart/form-data 文字列に対してエンコードしません。ファイルのアップロードウィジェットを含むフォームを使用するときはこの値が必要です。 + text/plain 空白を"+"記号に置き換えます。ただし、特殊文字に対してエンコードは行われません。 + +そのため、フォームのhtmlコードはこのようになります: + + + + ファイルアップロード + + +
+ + + +
+ + + +サーバでは、handlerFuncをひとつ追加します: + + http.HandleFunc("/upload", upload) + + // /uploadを処理するロジック + func upload(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) //リクエストを受け取るメソッド + if r.Method == "GET" { + crutime := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(crutime, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + t, _ := template.ParseFiles("upload.gtpl") + t.Execute(w, token) + } else { + r.ParseMultipartForm(32 << 20) + file, handler, err := r.FormFile("uploadfile") + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + fmt.Fprintf(w, "%v", handler.Header) + f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + fmt.Println(err) + return + } + defer f.Close() + io.Copy(f, file) + } + } + +上のコードでは、ファイルのアップロードを処理するためには`r.ParseMultipartForm`をコールする必要があります。引数には`maxMemory`が表示されています。`ParseMultipartForm`をコールした後、アップロードするファイルは`maxMemory`のサイズのメモリに保存されます。もしファイルのサイズが`maxMemory`を超えた場合、残った部分はシステムのテンポラリファイルに保存されます。`r.FormFile`によって上のファイルハンドルを取得することができます。その後実例の中では`io.Copy`を使ってファイルを保存しています。 + +>その他のファイルではないフィールド情報を取得する時は`r.ParseForm`をコールする必要はありません。必要な時はGoが自動でコールします。また`ParseMultipartFrom`を一度コールすると、その後にもう一度コールしても効果はありません。 + +上の実例を通して、ファイルのアップロードには主に3ステップの処理があることが分かります: + +1. フォームにenctype="multipart/form-data"を追加する。 +2. サーバで`r.ParseMultipartForm`をコールし、アップロードするファイルをメモリとテンポラリファイルに保存する。 +3. `r.FormFile`を使用して、ファイルハンドルを取得し、ファイルに対して保存等の処理を行う。 + +ファイルhandlerはmultipart.FileHnadlerです。この中には以下のような構造体が保存されています。 + + type FileHeader struct { + Filename string + Header textproto.MIMEHeader + // contains filtered or unexported fields + } + +上のコード例では以下のようにファイルのアップロードを出力します。 + +![](images/4.5.upload2.png?raw=true) + +図4.5 ファイルのアップロードを行った後サーバが受け取った情報の出力 + +## クライアントによるファイルのアップロード + +上の例でどのようにフォームからファイルをアップロードするのか示しました。その後サーバでファイルを処理しますが、Goは実はクライアントのフォームによるファイルのアップロードをエミュレートする機能をサポートしています。詳しい仕様方法は以下の例をご覧ください: + + package main + + import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + ) + + func postFile(filename string, targetUrl string) error { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + //キーとなる操作 + fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) + if err != nil { + fmt.Println("error writing to buffer") + return err + } + + //ファイルハンドル操作をオープンする + fh, err := os.Open(filename) + if err != nil { + fmt.Println("error opening file") + return err + } + + //iocopy + _, err = io.Copy(fileWriter, fh) + if err != nil { + return err + } + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + + resp, err := http.Post(targetUrl, contentType, bodyBuf) + if err != nil { + return err + } + defer resp.Body.Close() + resp_body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + fmt.Println(resp.Status) + fmt.Println(string(resp_body)) + return nil + } + + // sample usage + func main() { + target_url := "http://localhost:9090/upload" + filename := "./astaxie.pdf" + postFile(filename, target_url) + } + + +上の例ではクライアントが如何にサーバに対し一つのファイルをアップロードするのかご説明しました。クライアントはmultipart.Writeを通してファイルの本文をバッファの中に書き込みます。その後、httpのPostメソッドをコールしてバッファからサーバに転送します。 + +>もしあなたが他にusernameといった普通のフィールドを同時に書き込む場合は、multipartのWriteFieldメソッドをコールして、その他の似たようなフィールドを複数書き込むことができます。 + +## links + * [目次]() + * 前へ: [フォームの多重送信の防止](<04.4.md>) + * 次へ: [概要](<04.6.md>) diff --git a/ja/ebook/04.6.md b/ja/ebook/04.6.md index 3529a084..e3b349da 100644 --- a/ja/ebook/04.6.md +++ b/ja/ebook/04.6.md @@ -1,9 +1,9 @@ -# 4.6 概要 -この一章ではGoでどのようにフォームの情報を処理するか学びました。ユーザのログインから、ファイルのアップロードの例で、Goがformの情報およびファイルをアップロードする手段についてご説明しました。しかし、フォームを処理する過程ではユーザの入力した情報を懸賞する必要があります。ホームページのセキュリティの重要性を考慮すると、データのフィルタリングは相当重要です。そのため、以降の章では異なる方面のデータフィルタリングをご説明します。同時にGoの文字列に対する正規表現についても述べます。 - -この一章を通してクライアントとサーバが如何にデータを互いにやりとりするか理解いただけたと思います。クライアントはデータをサーバシステムに渡し、サーバはデータを受け取って結果をクライアントにフィードバックします。 - -## links - * [目次]() - * 前へ: [ファイルのアップロードの処理](<04.5.md>) - * 次へ: [データベースへのアクセス](<05.0.md>) +# 4.6 概要 +この一章ではGoでどのようにフォームの情報を処理するか学びました。ユーザのログインから、ファイルのアップロードの例で、Goがformの情報およびファイルをアップロードする手段についてご説明しました。しかし、フォームを処理する過程ではユーザの入力した情報を懸賞する必要があります。ホームページのセキュリティの重要性を考慮すると、データのフィルタリングは相当重要です。そのため、以降の章では異なる方面のデータフィルタリングをご説明します。同時にGoの文字列に対する正規表現についても述べます。 + +この一章を通してクライアントとサーバが如何にデータを互いにやりとりするか理解いただけたと思います。クライアントはデータをサーバシステムに渡し、サーバはデータを受け取って結果をクライアントにフィードバックします。 + +## links + * [目次]() + * 前へ: [ファイルのアップロードの処理](<04.5.md>) + * 次へ: [データベースへのアクセス](<05.0.md>) diff --git a/ja/ebook/05.0.md b/ja/ebook/05.0.md index b215ef0f..cf8108e8 100644 --- a/ja/ebook/05.0.md +++ b/ja/ebook/05.0.md @@ -1,14 +1,14 @@ -# 5 データベースへのアクセス -多くのWebアプリケーションプログラムにおいて、データベースはその核心となるものです。データベースはあなたが検索やさまざまな情報を修正したい場合にはほとんどで使用されます。例えばユーザ情報や製品の目録またはニュースのリスト等です。 - -Goはどのようなデータベースの使用もbuiltinではサポートされていません。しかし、Goはdatabase/sqlインターフェースを定義していますので、ユーザはこのドライバインターフェースに基いて目的のデータベースを使用することができます。5.1節でGoでデザインされているいくつかのドライバインターフェースやデータベースドライバインターフェースをご紹介します。5.2から5.4節では現在比較的使用されている関係型データドリブンとその使用方法についてご紹介します。5.5節では私が開発したORMライブラリをご紹介します。database/sqlの標準インターフェースに基づいた開発です。ほぼ全てのdatabase/sqlをサポートするデータベースの使用に互換性があります。Goスタイルで簡単にデータベース操作を行うことができます。 - -現在NOSQLはすでにWeb開発の流行となっています。多くのアプリケーションがNOSQLをデータベースとして採用しています。以前のキャッシュではありません。5.6節ではMongoDBとRedisの2つのNOSQLデータベースについてご紹介します。 - -## 目次 - ![](images/navi5.png?raw=true) - -## links - * [目次]() - * 前へ: [第四章概要](<04.6.md>) - * 次へ: [database/sqlインターフェース](<05.1.md>) +# 5 データベースへのアクセス +多くのWebアプリケーションプログラムにおいて、データベースはその核心となるものです。データベースはあなたが検索やさまざまな情報を修正したい場合にはほとんどで使用されます。例えばユーザ情報や製品の目録またはニュースのリスト等です。 + +Goはどのようなデータベースの使用もbuiltinではサポートされていません。しかし、Goはdatabase/sqlインターフェースを定義していますので、ユーザはこのドライバインターフェースに基いて目的のデータベースを使用することができます。5.1節でGoでデザインされているいくつかのドライバインターフェースやデータベースドライバインターフェースをご紹介します。5.2から5.4節では現在比較的使用されている関係型データドリブンとその使用方法についてご紹介します。5.5節では私が開発したORMライブラリをご紹介します。database/sqlの標準インターフェースに基づいた開発です。ほぼ全てのdatabase/sqlをサポートするデータベースの使用に互換性があります。Goスタイルで簡単にデータベース操作を行うことができます。 + +現在NOSQLはすでにWeb開発の流行となっています。多くのアプリケーションがNOSQLをデータベースとして採用しています。以前のキャッシュではありません。5.6節ではMongoDBとRedisの2つのNOSQLデータベースについてご紹介します。 + +## 目次 + ![](images/navi5.png?raw=true) + +## links + * [目次]() + * 前へ: [第四章概要](<04.6.md>) + * 次へ: [database/sqlインターフェース](<05.1.md>) diff --git a/ja/ebook/05.3.md b/ja/ebook/05.3.md index 90796aa2..736a2687 100644 --- a/ja/ebook/05.3.md +++ b/ja/ebook/05.3.md @@ -1,118 +1,118 @@ -# 5.3 SQLiteデータベースの使用 - -SQLiteはオープンソースの組み込み式リレーショナルデータベースです。独立しており、設定なしでトランザクションのSQLデータベースエンジンをサポートします。非常にポータブルで簡単に利用でき、コンパクトで効率が高く、信頼性があります。他のデータベース管理システムとは異なり、SQLiteのインストールと実行は非常に簡単です。多くの場合は、ただSQLiteのバイナリファイルを用意するだけですぐに作成、接続、使用することができます。もしあなたが現在組み込み式データベースかソリューションをお探しであれば、SQLいては絶対に考慮するに値します。SQLiteはいわばオープンソースのAccessのようなものです。 - -## ドライバ -Goがサポートするsqliteのドライバも比較的多いのですが、大部分はdatabase/sqlインターフェースをサポートしていません。 - -- https://github.com/mattn/go-sqlite3 database/sqlインターフェースをサポートしています。cgo(cgoに関する情報はオフィシャルドキュメントかこの本の最後の章をご参考ください)に基づいて記述されています。 -- https://github.com/feyeleanor/gosqlite3 database/sqlインターフェースをサポートしていません。cgoに基いて記述されています。 -- https://github.com/phf/go-sqlite3 database/sqlインターフェースをサポートしていません。cgoに基いて記述されています。 - -現在database/sqlをサポートしているSQLいてデータベースドライバは一つ目だけです。私も現在これを採用してプロジェクトで開発しています。標準インターフェースを採用することは今後より良いドライバが開発された時に移行できることです。 - -## 実例コード -例に示すデータベーススキーマは以下の通りです。対応するテーブル作成SQL: - - CREATE TABLE `userinfo` ( - `uid` INTEGER PRIMARY KEY AUTOINCREMENT, - `username` VARCHAR(64) NULL, - `departname` VARCHAR(64) NULL, - `created` DATE NULL - ); - - CREATE TABLE `userdeatail` ( - `uid` INT(10) NULL, - `intro` TEXT NULL, - `profile` TEXT NULL, - PRIMARY KEY (`uid`) - ); - -下のGoプログラムがどのようにデータベースのテーブルのデータを操作するか見てみましょう:追加・削除・修正・検索 - - package main - - import ( - "database/sql" - "fmt" - _ "github.com/mattn/go-sqlite3" - ) - - func main() { - db, err := sql.Open("sqlite3", "./foo.db") - checkErr(err) - - //データの挿入 - stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)") - checkErr(err) - - res, err := stmt.Exec("astaxie", "研究開発部門", "2012-12-09") - checkErr(err) - - id, err := res.LastInsertId() - checkErr(err) - - fmt.Println(id) - //データの更新 - stmt, err = db.Prepare("update userinfo set username=? where uid=?") - checkErr(err) - - res, err = stmt.Exec("astaxieupdate", id) - checkErr(err) - - affect, err := res.RowsAffected() - checkErr(err) - - fmt.Println(affect) - - //データの検索 - rows, err := db.Query("SELECT * FROM userinfo") - checkErr(err) - - for rows.Next() { - var uid int - var username string - var department string - var created string - err = rows.Scan(&uid, &username, &department, &created) - checkErr(err) - fmt.Println(uid) - fmt.Println(username) - fmt.Println(department) - fmt.Println(created) - } - - //データの削除 - stmt, err = db.Prepare("delete from userinfo where uid=?") - checkErr(err) - - res, err = stmt.Exec(id) - checkErr(err) - - affect, err = res.RowsAffected() - checkErr(err) - - fmt.Println(affect) - - db.Close() - - } - - func checkErr(err error) { - if err != nil { - panic(err) - } - } - - -上のコードとMySQLの例の中のコードはほとんどまったく同じです。唯一異なるのはドライバのインポート部分です。`sql.Open`のコールではSQLiteの方法で開きます。 - - ->sqlite管理ツール:http://sqliteadmin.orbmu2k.de/ - ->簡単にデータベース管理を新規作成することができます。 - -## links - * [目次]() - * 前へ: [MySQLデータベースの使用](<05.2.md>) - * 次へ: [PostgreSQLデータベースの使用](<05.4.md>) +# 5.3 SQLiteデータベースの使用 + +SQLiteはオープンソースの組み込み式リレーショナルデータベースです。独立しており、設定なしでトランザクションのSQLデータベースエンジンをサポートします。非常にポータブルで簡単に利用でき、コンパクトで効率が高く、信頼性があります。他のデータベース管理システムとは異なり、SQLiteのインストールと実行は非常に簡単です。多くの場合は、ただSQLiteのバイナリファイルを用意するだけですぐに作成、接続、使用することができます。もしあなたが現在組み込み式データベースかソリューションをお探しであれば、SQLいては絶対に考慮するに値します。SQLiteはいわばオープンソースのAccessのようなものです。 + +## ドライバ +Goがサポートするsqliteのドライバも比較的多いのですが、大部分はdatabase/sqlインターフェースをサポートしていません。 + +- https://github.com/mattn/go-sqlite3 database/sqlインターフェースをサポートしています。cgo(cgoに関する情報はオフィシャルドキュメントかこの本の最後の章をご参考ください)に基づいて記述されています。 +- https://github.com/feyeleanor/gosqlite3 database/sqlインターフェースをサポートしていません。cgoに基いて記述されています。 +- https://github.com/phf/go-sqlite3 database/sqlインターフェースをサポートしていません。cgoに基いて記述されています。 + +現在database/sqlをサポートしているSQLいてデータベースドライバは一つ目だけです。私も現在これを採用してプロジェクトで開発しています。標準インターフェースを採用することは今後より良いドライバが開発された時に移行できることです。 + +## 実例コード +例に示すデータベーススキーマは以下の通りです。対応するテーブル作成SQL: + + CREATE TABLE `userinfo` ( + `uid` INTEGER PRIMARY KEY AUTOINCREMENT, + `username` VARCHAR(64) NULL, + `departname` VARCHAR(64) NULL, + `created` DATE NULL + ); + + CREATE TABLE `userdeatail` ( + `uid` INT(10) NULL, + `intro` TEXT NULL, + `profile` TEXT NULL, + PRIMARY KEY (`uid`) + ); + +下のGoプログラムがどのようにデータベースのテーブルのデータを操作するか見てみましょう:追加・削除・修正・検索 + + package main + + import ( + "database/sql" + "fmt" + _ "github.com/mattn/go-sqlite3" + ) + + func main() { + db, err := sql.Open("sqlite3", "./foo.db") + checkErr(err) + + //データの挿入 + stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)") + checkErr(err) + + res, err := stmt.Exec("astaxie", "研究開発部門", "2012-12-09") + checkErr(err) + + id, err := res.LastInsertId() + checkErr(err) + + fmt.Println(id) + //データの更新 + stmt, err = db.Prepare("update userinfo set username=? where uid=?") + checkErr(err) + + res, err = stmt.Exec("astaxieupdate", id) + checkErr(err) + + affect, err := res.RowsAffected() + checkErr(err) + + fmt.Println(affect) + + //データの検索 + rows, err := db.Query("SELECT * FROM userinfo") + checkErr(err) + + for rows.Next() { + var uid int + var username string + var department string + var created string + err = rows.Scan(&uid, &username, &department, &created) + checkErr(err) + fmt.Println(uid) + fmt.Println(username) + fmt.Println(department) + fmt.Println(created) + } + + //データの削除 + stmt, err = db.Prepare("delete from userinfo where uid=?") + checkErr(err) + + res, err = stmt.Exec(id) + checkErr(err) + + affect, err = res.RowsAffected() + checkErr(err) + + fmt.Println(affect) + + db.Close() + + } + + func checkErr(err error) { + if err != nil { + panic(err) + } + } + + +上のコードとMySQLの例の中のコードはほとんどまったく同じです。唯一異なるのはドライバのインポート部分です。`sql.Open`のコールではSQLiteの方法で開きます。 + + +>sqlite管理ツール:http://sqliteadmin.orbmu2k.de/ + +>簡単にデータベース管理を新規作成することができます。 + +## links + * [目次]() + * 前へ: [MySQLデータベースの使用](<05.2.md>) + * 次へ: [PostgreSQLデータベースの使用](<05.4.md>) diff --git a/ja/ebook/05.7.md b/ja/ebook/05.7.md index 0dd461ef..33371d44 100644 --- a/ja/ebook/05.7.md +++ b/ja/ebook/05.7.md @@ -1,9 +1,9 @@ -# 5.7 概要 -この章ではGoがどのようにdatabase/sqlインターフェースを設計するのかご説明しました。その後、サードパーティによるリレーショナルデータベースドライバの使用をご紹介しました。次にbeedbというリレーショナルデータベースに基づくORMライブラリがどのようにしてデータベースに対して簡単な操作を行うかご説明しました。最後にNOSQLのいくつかの知識をご紹介しました。現在GoのNOSQLに対するサポートはなかなかよくできています。Goは21世紀のC言語ですから、21世紀のデータベースに対するサポートも非常によくできています。 - -この一章の学習を通じて、いろいろなデータベースをどのように操作するか学んできました。Webの中でも重要なデータの保存に関する問題が解決したので、みなさんのdatabase/sqlの設計思想により一歩進んだ理解が深まることを願っています。 - -## links - * [目次]() - * 前へ: [NOSQLデータベースの操作](<05.6.md>) - * 次へ: [sessionとデータの保存](<06.0.md>) +# 5.7 概要 +この章ではGoがどのようにdatabase/sqlインターフェースを設計するのかご説明しました。その後、サードパーティによるリレーショナルデータベースドライバの使用をご紹介しました。次にbeedbというリレーショナルデータベースに基づくORMライブラリがどのようにしてデータベースに対して簡単な操作を行うかご説明しました。最後にNOSQLのいくつかの知識をご紹介しました。現在GoのNOSQLに対するサポートはなかなかよくできています。Goは21世紀のC言語ですから、21世紀のデータベースに対するサポートも非常によくできています。 + +この一章の学習を通じて、いろいろなデータベースをどのように操作するか学んできました。Webの中でも重要なデータの保存に関する問題が解決したので、みなさんのdatabase/sqlの設計思想により一歩進んだ理解が深まることを願っています。 + +## links + * [目次]() + * 前へ: [NOSQLデータベースの操作](<05.6.md>) + * 次へ: [sessionとデータの保存](<06.0.md>) diff --git a/ja/ebook/06.0.md b/ja/ebook/06.0.md index 1971e6c2..f785a159 100644 --- a/ja/ebook/06.0.md +++ b/ja/ebook/06.0.md @@ -1,12 +1,12 @@ -# 6 sessionとデータの保存 -Web開発ではどのようにしてユーザの閲覧家庭のすべてをコントロールするかということは非常に重要です。HTTPプロトコルはステートレスですので、ユーザの毎回のリクエストにはステータスがありません。Web操作の全体の過程の中でどの接続がどのユーザと関係しているのか知る方法がありません。では、どのようにしてこの問題を解決しているのでしょうか?Webでの伝統的な解決方法はcookieとsessionです。cookieによるメカニズムはクライアント側でのメカニズムです。ユーザのデータをクライアントに保存します。sessionメカニズムはサーバ側でのメカニズムです。サーバはハッシュテーブルのような構造でデータを保存します。ホームページの各閲覧者はユニークなIDを与えられます。すなわち、SessionIDです。この保存形式は2つだけです:urlによって渡されるか、クライアントのcookieに保存されるかです。当然、Sessionをデータベースに保存することもできます。よりセキュリティが高まりますが、効率の面ではいくつか後退します。 - -6.1節ではsessionメカニズムとcookieメカニズムの関係と区別についてご紹介します。6.2ではGo言語がどのようにsessionを実現しているかご説明します。この中では簡単なsessionマネージャを実現します。6.3節ではどのようにしてsessionハイジャックの状態を防ぐかご説明します。どのように効果的にsessionを保護するのか。sessionはそもそもどのようなところに保存してもよいのです。6.3節ではsessionをメモリの中に保存しますが、我々のアプリケーションをもう一歩展開させる場合、アプリケーションのsession共有を実現する必要があります。sessionをデータベースの中(memcacheまたはredis)に保存します。6.4節ではどのようにしてこの機能を実装するかご説明します。 - -## 目次 - ![](images/navi6.png?raw=true) - -## links - * [目次]() - * 前へ: [第五章概要](<05.7.md>) - * 次へ: [sessionとcookie](<06.1.md>) +# 6 sessionとデータの保存 +Web開発ではどのようにしてユーザの閲覧家庭のすべてをコントロールするかということは非常に重要です。HTTPプロトコルはステートレスですので、ユーザの毎回のリクエストにはステータスがありません。Web操作の全体の過程の中でどの接続がどのユーザと関係しているのか知る方法がありません。では、どのようにしてこの問題を解決しているのでしょうか?Webでの伝統的な解決方法はcookieとsessionです。cookieによるメカニズムはクライアント側でのメカニズムです。ユーザのデータをクライアントに保存します。sessionメカニズムはサーバ側でのメカニズムです。サーバはハッシュテーブルのような構造でデータを保存します。ホームページの各閲覧者はユニークなIDを与えられます。すなわち、SessionIDです。この保存形式は2つだけです:urlによって渡されるか、クライアントのcookieに保存されるかです。当然、Sessionをデータベースに保存することもできます。よりセキュリティが高まりますが、効率の面ではいくつか後退します。 + +6.1節ではsessionメカニズムとcookieメカニズムの関係と区別についてご紹介します。6.2ではGo言語がどのようにsessionを実現しているかご説明します。この中では簡単なsessionマネージャを実現します。6.3節ではどのようにしてsessionハイジャックの状態を防ぐかご説明します。どのように効果的にsessionを保護するのか。sessionはそもそもどのようなところに保存してもよいのです。6.3節ではsessionをメモリの中に保存しますが、我々のアプリケーションをもう一歩展開させる場合、アプリケーションのsession共有を実現する必要があります。sessionをデータベースの中(memcacheまたはredis)に保存します。6.4節ではどのようにしてこの機能を実装するかご説明します。 + +## 目次 + ![](images/navi6.png?raw=true) + +## links + * [目次]() + * 前へ: [第五章概要](<05.7.md>) + * 次へ: [sessionとcookie](<06.1.md>) diff --git a/ja/ebook/preface.md b/ja/ebook/preface.md index 77fd29f1..9ef1d440 100644 --- a/ja/ebook/preface.md +++ b/ja/ebook/preface.md @@ -1,96 +1,96 @@ -* 1.[Goの環境設定](01.0.md) - - 1.1. [Goのインストール](01.1.md) - - 1.2. [GOPATHとワーキングディレクトリ](01.2.md) - - 1.3. [Goのコマンド](01.3.md) - - 1.4. [Goの開発ツール](01.4.md) - - 1.5. [概要](01.5.md) -* 2.[Go言語の基礎](02.0.md) - - 2.1. [こんにちは、Go](02.1.md) - - 2.2. [Goの基礎](02.2.md) - - 2.3. [フローと関数](02.3.md) - - 2.4. [struct型](02.4.md) - - 2.5. [オブジェクト指向](02.5.md) - - 2.6. [interface](02.6.md) - - 2.7. [マルチスレッド](02.7.md) - - 2.8. [概要](02.8.md) -* 3.[Webの基礎](03.0.md) - - 3.1 [webでの作業方法](03.1.md) - - 3.2 [Goで簡単なwebサーバを立てる](03.2.md) - - 3.3 [Goはどのようにしてweb作業を行うか](03.3.md) - - 3.4 [Goのhttpパッケージ詳細](03.4.md) - - 3.5 [概要](03.5.md) -* 4.[フォーム](04.0.md) - - 4.1 [フォームの入力を処理する](04.1.md) - - 4.2 [フォームに入力された内容の検証](04.2.md) - - 4.3 [クロスサイトスクリプティングの予防](04.3.md) - - 4.4 [フォームの複数回送信の防止](04.4.md) - - 4.5 [ファイルのアップロード処理](04.5.md) - - 4.6 [概要](04.6.md) -* 5.[データベースへのアクセス](05.0.md) - - 5.1 [database/sqlインターフェース](05.1.md) - - 5.2 [MySQL データベースの使用](05.2.md) - - 5.3 [SQLiteデータベースの使用](05.3.md) - - 5.4 [PostgreSQLデータベースの使用](05.4.md) - - 5.5 [beedbライブラリを使用してORM開発を行う](05.5.md) - - 5.6 [NOSQLデータベースの操作](05.6.md) - - 5.7 [概要](05.7.md) -* 6.[sessionとデータの保存](06.0.md) - - 6.1 [sessionとcookie](06.1.md) - - 6.2 [Goはどのようにしてsessionを使用するか](06.2.md) - - 6.3 [sessionストレージ](06.3.md) - - 6.4 [sessionハイジャックの予防](06.4.md) - - 6.5 [概要](06.5.md) -* 7.[テキスト処理](07.0.md) - - 7.1 [XMLの処理](07.1.md) - - 7.2 [JSONの処理](07.2.md) - - 7.3 [正規表現の処理](07.3.md) - - 7.4 [テンプレートの処理](07.4.md) - - 7.5 [ファイルの操作](07.5.md) - - 7.6 [文字列の処理](07.6.md) - - 7.7 [概要](07.7.md) -* 8.[Webサービス](08.0.md) - - 8.1 [Socketプログラミング](08.1.md) - - 8.2 [WebSocket](08.2.md) - - 8.3 [REST](08.3.md) - - 8.4 [RPC](08.4.md) - - 8.5 [概要](08.5.md) -* 9.[セキュリティと暗号化](09.0.md) - - 9.1 [CSRF攻撃の予防](09.1.md) - - 9.2 [入力フィルタリングの確保](09.2.md) - - 9.3 [XSS攻撃の回避](09.3.md) - - 9.4 [SQLインジェクションの回避](09.4.md) - - 9.5 [パスワードの保存](09.5.md) - - 9.6 [データを暗号化/復元する](09.6.md) - - 9.7 [概要](09.7.md) -* 10.[国際化とローカライズ](10.0.md) - - 10.1 [デフォルトロケールの設定](10.1.md) - - 10.2 [ローカライズリソース](10.2.md) - - 10.3 [国際化サイト](10.3.md) - - 10.4 [概要](10.4.md) -* 11.[エラー処理、デバッグとテスト](11.0.md) - - 11.1 [エラー処理](11.1.md) - - 11.2 [GDBを使用したデバッグ](11.2.md) - - 11.3 [Goによるテスト例](11.3.md) - - 11.4 [概要](11.4.md) -* 12.[デプロイとメンテナンス](12.0.md) - - 12.1 [アプリケーションログ](12.1.md) - - 12.2 [サイトのエラー処理](12.2.md) - - 12.3 [アプリケーションのデプロイ](12.3.md) - - 12.4 [バックアップとリストア](12.4.md) - - 12.5 [概要](12.5.md) -* 13.[どのようにしてWebフレームワークを設計するか](13.0.md)  - - 13.1 [プロジェクトプラン](13.1.md)  - - 13.2 [カスタムルータの設計](13.2.md) - - 13.3 [controllerの設計](13.3.md) - - 13.4 [ログとデプロイ設計](13.4.md) - - 13.5 [ブログの追加/削除/修正の実装](13.5.md) - - 13.6 [概要](13.6.md)  -* 14.[Webフレームワークの拡張](14.0.md) - - 14.1 [静的ファイルのサポート](14.1.md) - - 14.2 [Sessionのサポート](14.2.md) - - 14.3 [フォームのサポート](14.3.md) - - 14.4 [ユーザ認証](14.4.md) - - 14.5 [多言語サポート](14.5.md) - - 14.6 [pprofのサポート](14.6.md) - - 14.7 [概要](14.7.md) -* 付録A [参考資料](ref.md) +* 1.[Goの環境設定](01.0.md) + - 1.1. [Goのインストール](01.1.md) + - 1.2. [GOPATHとワーキングディレクトリ](01.2.md) + - 1.3. [Goのコマンド](01.3.md) + - 1.4. [Goの開発ツール](01.4.md) + - 1.5. [概要](01.5.md) +* 2.[Go言語の基礎](02.0.md) + - 2.1. [こんにちは、Go](02.1.md) + - 2.2. [Goの基礎](02.2.md) + - 2.3. [フローと関数](02.3.md) + - 2.4. [struct型](02.4.md) + - 2.5. [オブジェクト指向](02.5.md) + - 2.6. [interface](02.6.md) + - 2.7. [マルチスレッド](02.7.md) + - 2.8. [概要](02.8.md) +* 3.[Webの基礎](03.0.md) + - 3.1 [webでの作業方法](03.1.md) + - 3.2 [Goで簡単なwebサーバを立てる](03.2.md) + - 3.3 [Goはどのようにしてweb作業を行うか](03.3.md) + - 3.4 [Goのhttpパッケージ詳細](03.4.md) + - 3.5 [概要](03.5.md) +* 4.[フォーム](04.0.md) + - 4.1 [フォームの入力を処理する](04.1.md) + - 4.2 [フォームに入力された内容の検証](04.2.md) + - 4.3 [クロスサイトスクリプティングの予防](04.3.md) + - 4.4 [フォームの複数回送信の防止](04.4.md) + - 4.5 [ファイルのアップロード処理](04.5.md) + - 4.6 [概要](04.6.md) +* 5.[データベースへのアクセス](05.0.md) + - 5.1 [database/sqlインターフェース](05.1.md) + - 5.2 [MySQL データベースの使用](05.2.md) + - 5.3 [SQLiteデータベースの使用](05.3.md) + - 5.4 [PostgreSQLデータベースの使用](05.4.md) + - 5.5 [beedbライブラリを使用してORM開発を行う](05.5.md) + - 5.6 [NOSQLデータベースの操作](05.6.md) + - 5.7 [概要](05.7.md) +* 6.[sessionとデータの保存](06.0.md) + - 6.1 [sessionとcookie](06.1.md) + - 6.2 [Goはどのようにしてsessionを使用するか](06.2.md) + - 6.3 [sessionストレージ](06.3.md) + - 6.4 [sessionハイジャックの予防](06.4.md) + - 6.5 [概要](06.5.md) +* 7.[テキスト処理](07.0.md) + - 7.1 [XMLの処理](07.1.md) + - 7.2 [JSONの処理](07.2.md) + - 7.3 [正規表現の処理](07.3.md) + - 7.4 [テンプレートの処理](07.4.md) + - 7.5 [ファイルの操作](07.5.md) + - 7.6 [文字列の処理](07.6.md) + - 7.7 [概要](07.7.md) +* 8.[Webサービス](08.0.md) + - 8.1 [Socketプログラミング](08.1.md) + - 8.2 [WebSocket](08.2.md) + - 8.3 [REST](08.3.md) + - 8.4 [RPC](08.4.md) + - 8.5 [概要](08.5.md) +* 9.[セキュリティと暗号化](09.0.md) + - 9.1 [CSRF攻撃の予防](09.1.md) + - 9.2 [入力フィルタリングの確保](09.2.md) + - 9.3 [XSS攻撃の回避](09.3.md) + - 9.4 [SQLインジェクションの回避](09.4.md) + - 9.5 [パスワードの保存](09.5.md) + - 9.6 [データを暗号化/復元する](09.6.md) + - 9.7 [概要](09.7.md) +* 10.[国際化とローカライズ](10.0.md) + - 10.1 [デフォルトロケールの設定](10.1.md) + - 10.2 [ローカライズリソース](10.2.md) + - 10.3 [国際化サイト](10.3.md) + - 10.4 [概要](10.4.md) +* 11.[エラー処理、デバッグとテスト](11.0.md) + - 11.1 [エラー処理](11.1.md) + - 11.2 [GDBを使用したデバッグ](11.2.md) + - 11.3 [Goによるテスト例](11.3.md) + - 11.4 [概要](11.4.md) +* 12.[デプロイとメンテナンス](12.0.md) + - 12.1 [アプリケーションログ](12.1.md) + - 12.2 [サイトのエラー処理](12.2.md) + - 12.3 [アプリケーションのデプロイ](12.3.md) + - 12.4 [バックアップとリストア](12.4.md) + - 12.5 [概要](12.5.md) +* 13.[どのようにしてWebフレームワークを設計するか](13.0.md)  + - 13.1 [プロジェクトプラン](13.1.md)  + - 13.2 [カスタムルータの設計](13.2.md) + - 13.3 [controllerの設計](13.3.md) + - 13.4 [ログとデプロイ設計](13.4.md) + - 13.5 [ブログの追加/削除/修正の実装](13.5.md) + - 13.6 [概要](13.6.md)  +* 14.[Webフレームワークの拡張](14.0.md) + - 14.1 [静的ファイルのサポート](14.1.md) + - 14.2 [Sessionのサポート](14.2.md) + - 14.3 [フォームのサポート](14.3.md) + - 14.4 [ユーザ認証](14.4.md) + - 14.5 [多言語サポート](14.5.md) + - 14.6 [pprofのサポート](14.6.md) + - 14.7 [概要](14.7.md) +* 付録A [参考資料](ref.md)