[ja] keep crlf in original documents

This commit is contained in:
Shin Kojima
2013-12-26 22:19:49 +09:00
parent 2a957e1433
commit 0d50fa1140
18 changed files with 1402 additions and 1402 deletions

View File

@@ -1,23 +1,23 @@
# 1 GOの環境設定
Goの世界へようこそ、さっそく初めてみましょう
Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にプログラムできる言語です。以下のような特徴を持っています
- 一台のコンピュータ上であっという間に大型のGoプログラムを作り出すことができます。
- Goはソフトウェアの構造にモデルを提供します。分析をより簡単にこなせ、Cスタイルの頭にありがちなファイルとライブラリのincludeの大部分を省くことができます。
- Goは静的型付け言語です。型には階層がありません。このためユーザは型の定義の関係に時間をとられることなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
- Goは完全にガベージコレクションタイプの言語です。また、並列処理とネットワークを基本的にサポートしています。
- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。
Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、モダンにネットワークとマルチプロセッサもサポートしています。これらの目標を達成するには、解決しなければならない言語上の問題がいくつかあります表現力豊かだけれども軽いシステム、並列処理とガベージコレクション、厳格な依存定義などです。これらはライブラリやツール無しにはうまくいきません。Goもその要望に応えます。
この章ではGoのインストール方法と設定について述べます。
## 目次
![](images/navi1.png?raw=true)
## links
* [目次](<preface.md>)
* 次へ: [Goのインストール](<01.1.md>)
# 1 GOの環境設定
Goの世界へようこそ、さっそく初めてみましょう
Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にプログラムできる言語です。以下のような特徴を持っています
- 一台のコンピュータ上であっという間に大型のGoプログラムを作り出すことができます。
- Goはソフトウェアの構造にモデルを提供します。分析をより簡単にこなせ、Cスタイルの頭にありがちなファイルとライブラリのincludeの大部分を省くことができます。
- Goは静的型付け言語です。型には階層がありません。このためユーザは型の定義の関係に時間をとられることなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
- Goは完全にガベージコレクションタイプの言語です。また、並列処理とネットワークを基本的にサポートしています。
- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。
Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、モダンにネットワークとマルチプロセッサもサポートしています。これらの目標を達成するには、解決しなければならない言語上の問題がいくつかあります表現力豊かだけれども軽いシステム、並列処理とガベージコレクション、厳格な依存定義などです。これらはライブラリやツール無しにはうまくいきません。Goもその要望に応えます。
この章ではGoのインストール方法と設定について述べます。
## 目次
![](images/navi1.png?raw=true)
## links
* [目次](<preface.md>)
* 次へ: [Goのインストール](<01.1.md>)

View File

@@ -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
* [目次](<preface.md>)
* 前へ: [第一章概要](<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
* [目次](<preface.md>)
* 前へ: [第一章概要](<01.5.md>)
* 次へ: [こんにちは、Go](<02.1.md>)

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,31 @@
# 2.8 概要
この章では主にGo言語のいくつかの文法をご紹介しました。文法を通してGoがいかに簡単かご覧いただけたかと思います。たった個のキーワードです。もう一度これらキーワードが何に使われるのか見てみることにしましょう。
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データの読み込みに用いられます。
この個のキーワードを覚えれば、Goは既に殆ど学び終わったも同然です。
## links
* [目次](<preface.md>)
* 前へ: [マルチスレッド](<02.7.md>)
* 次へ: [Webの基礎](<03.0.md>)
# 2.8 概要
この章では主にGo言語のいくつかの文法をご紹介しました。文法を通してGoがいかに簡単かご覧いただけたかと思います。たった個のキーワードです。もう一度これらキーワードが何に使われるのか見てみることにしましょう。
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データの読み込みに用いられます。
この個のキーワードを覚えれば、Goは既に殆ど学び終わったも同然です。
## links
* [目次](<preface.md>)
* 前へ: [マルチスレッド](<02.7.md>)
* 次へ: [Webの基礎](<03.0.md>)

View File

@@ -1,11 +1,11 @@
# 3 Webの基礎
Webのプログラミングの基礎を勉強することはあなたにとってまさにこの本を読む理由でしょう。事実、どのようにGoを使ってWebアプリケーションをプログラムするかはこの本の目的でもあります。前回までで、Goは既に成熟したHttp処理パッケージを持つことをご紹介しました。これによってどのような事情の動的なWebプログラミングも簡単に作成できます。これ以降の章でご紹介する内容はどれもWebプログラミングの範疇です。この章ではWebに関する概念とGo言語がいかにWebプログラムを実行するかに集中して討論します。
## 目次
![](images/navi3.png?raw=true)
## links
* [目次](<preface.md>)
* 前へ: [第二章概要](<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
* [目次](<preface.md>)
* 前へ: [第二章概要](<02.8.md>)
* 次へ: [webでの作業方法](<03.1.md>)

View File

@@ -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解決の全体のプロセス
> いわゆる`再帰検索プロセス`は"検索者"の交代を意味します。また、`反復する検索プロセス`では"検索者"は不変です。
>
> 例をあげて説明しましょう。あなたは一緒に法律の授業を受けている女の子の電話番号を知りたいとします。あなたはこっそり彼女の写真も撮っています。寝室にもどって、正義感の強いアニキたちにそのことを伝えます。このアニキたちは異議を唱えることもなく、胸を叩いてあなたにこう言います。「急ぐ必要はない。私があなたに替わって調べてあげましょう」(この時一時再帰検索が完了しています。すなわち、検索者の役割が変更されました。)。その後彼は写真を携え学部の年生の先輩のところに聞きにいきます。「この女の子はxx学部なんですけど・・・」その後このアニキは矢継ぎ早にxx学部のオフィス主任の助手を務めているクラスメートに聞きます。このクラスメートはxx学部のyyゼミであると言います。またこの正義感の強いアニキたちはxx学部のyyゼミのゼミ長のところにいき、この女の子の電話番号をゲットします。ここまでで何回かの連続検索が完了しました。すなわち、検索者の役は変わっていませんが、聞きに行く対象を反復して取り替えています。最後に彼は番号をあなたの手に渡すことで全体の検索が完了します。
上のステップを通して、IPアドレスを最後に取得します。つまりブラウザが最後にリクエストを送る時はIPにもとづいて、サーバと情報のやりとりをするのです。
## HTTPプロトコル詳細
HTTPプロトコルはWeb作業の確信です。そのためWebの作業方法をくまなく理解するためには、HTTPがいったいどのような作業を行なっているのか深く理解する必要があります。
HTTPはWebサーバにブラウザ(クライアント)とInternetを通してデータをやり取りさせるプロトコルです。これはTCPプロトコルの上で成立しますので、一般的にはTCPの80番ポートが採用されます。これはリクエストとレスポンスのプロトコルです--クライアントはリクエストを送信しサーバがこのリクエストに対してレスポンスを行います。HTTPでは、クライアントは常に接続を行いHTTPリクエストを送信することでタスクをこなします。サーバは主導的にクライアントと接続することはできません。また、クライアントに対してコールバック接続を送信することもできません。クライアントとサーバは事前に接続を中断することができます。例えば、ブラウザでファイルをダウンロードする際、"停止"ボタンをクリックすることでファイルのダウンロードを中断し、サーバとのHTTP接続を閉じることができます。
HTTPプロトコルはステートレスです。同じクライアントの前のリクエストと今回のリクエストの間にはなんの対応関係もありません。HTTPサーバからすれば、このつのリクエストが同じクライアントから発せられたものかすらも知りません。この問題を解決するため、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プロトコル/プロトコルバージョン
Hostwww.iana.org //サーバのホスト名
User-AgentMozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //ブラウザ情報
Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //クライアントが受け取れるmime
Accept-Encodinggzip,deflate,sdch //ストリーム圧縮をサポートするか否か
Accept-CharsetUTF-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はこのリソースの検索、修正、増加、削除のつの操作に対応しています。よく見かけるのは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 //ボディの内容の長さ
//空行 ヘッダとボディを分けるために使われます。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //ボディ
Response包中的第一行叫做状态行由HTTP协议版本号 状态码, 状态消息 三部分组成。
ステータスコードはHTTPクライアントにHTTPサーバが事前にResponseを発生させるか伝えます。HTTP/1.1プロトコルでは種類のステータスコードが定義されています。ステータスコードは3桁の数字で表されます。はじめの数字はレスポンスの型を定義しています。
- 1XX 情報の表示 - リクエストの取得に成功しました。継続して処理します。
- 2XX 成功 - リクエストの取得に成功しました。わかりました。受け付けます。
- 3XX リダイレクション - リクエストを完了させる為に一歩進んだ処理が必要です。
- 4XX クライアントエラー - リクエストにシンタックスエラーまたはリクエストを実現できません。
- 5XX サーバーエラー - サーバは合法なリクエストを実現できません。
下の図で詳細なレスポンス情報について説明しております。左側にたくさんのリソースのレスポンスコードを見ることができます。200は通常です。正常なデータであることを意味しています。302ははリダイレクトを意味します。response headerの中には詳細な情報が展開されています。
![](images/3.1.response.png?raw=true)
図3.6 ホームページに一度訪問した場合のリクエスト情報のすべて
### HTTPプロトコルはステートレスでConnection: keep-aliveの区別
ステートレスとはプロトコルがタスク処理に対して記憶力を有していないことを意味します。サーバはクライアントがどんな状態にあるか知らず、別の角度から言えば、サーバ上のホームページを開いたのと、あなたが以前このサーバ上のホームページを開いた事との間には何の関係もないことを意味しています。
HTTPはステートレスなコネクション指向のプロトコルです。ステートレスとはHTTPがTCP接続を保持していないことを意味するものではありません。また、HTTPがUDPプロトコルを使っていることを示すものでもありません。コネクションロスに対して
HTTP/1.1から、デフォルトでKeep-Aliveがオンになっており、接続性が保持されます。簡単にいえば、あるホームページを開き終わった後、クライアントとサーバの間ではHTTPデータを転送するためのTCP接続は閉じません。もしクライアントが再度このサーバ上のホームページを開いた場合、すでに確立されたTCP接続を継続して使用し得ます。
Keep-Aliveは永久にコネクションを保持するものではありません。これには保持する時間があります。異なるサーバソフトウェア例えばApacheではこの時間を設定することができます。
## リクエスト実例
![](images/3.1.web.png?raw=true)
図3.7 一回の要求のrequestとresponse
上の図で全体の通信プロセスをご理解いただけるかと思います。同時に注意深い読者はひとつのURLリクエストにもかかわらず左のペインではどうしてこのように多くのリソース要求があるのかと思われたかもしれません。これらはすべて静的なファイルです。goは静的なファイルに対して専門的に処理する方法を有しています。
これはブラウザの機能の一つです。URLを一度要求すると、サーバはhtmlページを返します。その後ブラウザはHTMLを読み取り、HTMLのDOMの中にあるイメージリンク、cssスクリプトとjsスクリプトのリンクを見つけた時、ブラウザは自動的に静的なリソースのHTTP要求を行います。目的の静的なリソースを取得すると、ブラウザは読み始めます。最後にすべてのリソースを整理し、我々の前のスクリーン上に展開します。
>ホームページの改良では、HTTPのリクエスト回数を減らすことがあります。つまり、なるべく多くのcssとjsリソースを同じところに集めるのです。目的は出来る限りホームページの静的リソースのリクエスト回数を減少させる事にあります。ホームページの表示速度を上げると同時にサーバのバッファリングを減らす事ができます。
## links
* [目次](<preface.md>)
* 前へ: [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解決の全体のプロセス
> いわゆる`再帰検索プロセス`は"検索者"の交代を意味します。また、`反復する検索プロセス`では"検索者"は不変です。
>
> 例をあげて説明しましょう。あなたは一緒に法律の授業を受けている女の子の電話番号を知りたいとします。あなたはこっそり彼女の写真も撮っています。寝室にもどって、正義感の強いアニキたちにそのことを伝えます。このアニキたちは異議を唱えることもなく、胸を叩いてあなたにこう言います。「急ぐ必要はない。私があなたに替わって調べてあげましょう」(この時一時再帰検索が完了しています。すなわち、検索者の役割が変更されました。)。その後彼は写真を携え学部の年生の先輩のところに聞きにいきます。「この女の子はxx学部なんですけど・・・」その後このアニキは矢継ぎ早にxx学部のオフィス主任の助手を務めているクラスメートに聞きます。このクラスメートはxx学部のyyゼミであると言います。またこの正義感の強いアニキたちはxx学部のyyゼミのゼミ長のところにいき、この女の子の電話番号をゲットします。ここまでで何回かの連続検索が完了しました。すなわち、検索者の役は変わっていませんが、聞きに行く対象を反復して取り替えています。最後に彼は番号をあなたの手に渡すことで全体の検索が完了します。
上のステップを通して、IPアドレスを最後に取得します。つまりブラウザが最後にリクエストを送る時はIPにもとづいて、サーバと情報のやりとりをするのです。
## HTTPプロトコル詳細
HTTPプロトコルはWeb作業の確信です。そのためWebの作業方法をくまなく理解するためには、HTTPがいったいどのような作業を行なっているのか深く理解する必要があります。
HTTPはWebサーバにブラウザ(クライアント)とInternetを通してデータをやり取りさせるプロトコルです。これはTCPプロトコルの上で成立しますので、一般的にはTCPの80番ポートが採用されます。これはリクエストとレスポンスのプロトコルです--クライアントはリクエストを送信しサーバがこのリクエストに対してレスポンスを行います。HTTPでは、クライアントは常に接続を行いHTTPリクエストを送信することでタスクをこなします。サーバは主導的にクライアントと接続することはできません。また、クライアントに対してコールバック接続を送信することもできません。クライアントとサーバは事前に接続を中断することができます。例えば、ブラウザでファイルをダウンロードする際、"停止"ボタンをクリックすることでファイルのダウンロードを中断し、サーバとのHTTP接続を閉じることができます。
HTTPプロトコルはステートレスです。同じクライアントの前のリクエストと今回のリクエストの間にはなんの対応関係もありません。HTTPサーバからすれば、このつのリクエストが同じクライアントから発せられたものかすらも知りません。この問題を解決するため、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プロトコル/プロトコルバージョン
Hostwww.iana.org //サーバのホスト名
User-AgentMozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //ブラウザ情報
Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //クライアントが受け取れるmime
Accept-Encodinggzip,deflate,sdch //ストリーム圧縮をサポートするか否か
Accept-CharsetUTF-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はこのリソースの検索、修正、増加、削除のつの操作に対応しています。よく見かけるのは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 //ボディの内容の長さ
//空行 ヘッダとボディを分けるために使われます。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //ボディ
Response包中的第一行叫做状态行由HTTP协议版本号 状态码, 状态消息 三部分组成。
ステータスコードはHTTPクライアントにHTTPサーバが事前にResponseを発生させるか伝えます。HTTP/1.1プロトコルでは種類のステータスコードが定義されています。ステータスコードは3桁の数字で表されます。はじめの数字はレスポンスの型を定義しています。
- 1XX 情報の表示 - リクエストの取得に成功しました。継続して処理します。
- 2XX 成功 - リクエストの取得に成功しました。わかりました。受け付けます。
- 3XX リダイレクション - リクエストを完了させる為に一歩進んだ処理が必要です。
- 4XX クライアントエラー - リクエストにシンタックスエラーまたはリクエストを実現できません。
- 5XX サーバーエラー - サーバは合法なリクエストを実現できません。
下の図で詳細なレスポンス情報について説明しております。左側にたくさんのリソースのレスポンスコードを見ることができます。200は通常です。正常なデータであることを意味しています。302ははリダイレクトを意味します。response headerの中には詳細な情報が展開されています。
![](images/3.1.response.png?raw=true)
図3.6 ホームページに一度訪問した場合のリクエスト情報のすべて
### HTTPプロトコルはステートレスでConnection: keep-aliveの区別
ステートレスとはプロトコルがタスク処理に対して記憶力を有していないことを意味します。サーバはクライアントがどんな状態にあるか知らず、別の角度から言えば、サーバ上のホームページを開いたのと、あなたが以前このサーバ上のホームページを開いた事との間には何の関係もないことを意味しています。
HTTPはステートレスなコネクション指向のプロトコルです。ステートレスとはHTTPがTCP接続を保持していないことを意味するものではありません。また、HTTPがUDPプロトコルを使っていることを示すものでもありません。コネクションロスに対して
HTTP/1.1から、デフォルトでKeep-Aliveがオンになっており、接続性が保持されます。簡単にいえば、あるホームページを開き終わった後、クライアントとサーバの間ではHTTPデータを転送するためのTCP接続は閉じません。もしクライアントが再度このサーバ上のホームページを開いた場合、すでに確立されたTCP接続を継続して使用し得ます。
Keep-Aliveは永久にコネクションを保持するものではありません。これには保持する時間があります。異なるサーバソフトウェア例えばApacheではこの時間を設定することができます。
## リクエスト実例
![](images/3.1.web.png?raw=true)
図3.7 一回の要求のrequestとresponse
上の図で全体の通信プロセスをご理解いただけるかと思います。同時に注意深い読者はひとつのURLリクエストにもかかわらず左のペインではどうしてこのように多くのリソース要求があるのかと思われたかもしれません。これらはすべて静的なファイルです。goは静的なファイルに対して専門的に処理する方法を有しています。
これはブラウザの機能の一つです。URLを一度要求すると、サーバはhtmlページを返します。その後ブラウザはHTMLを読み取り、HTMLのDOMの中にあるイメージリンク、cssスクリプトとjsスクリプトのリンクを見つけた時、ブラウザは自動的に静的なリソースのHTTP要求を行います。目的の静的なリソースを取得すると、ブラウザは読み始めます。最後にすべてのリソースを整理し、我々の前のスクリーン上に展開します。
>ホームページの改良では、HTTPのリクエスト回数を減らすことがあります。つまり、なるべく多くのcssとjsリソースを同じところに集めるのです。目的は出来る限りホームページの静的リソースのリクエスト回数を減少させる事にあります。ホームページの表示速度を上げると同時にサーバのバッファリングを減らす事ができます。
## links
* [目次](<preface.md>)
* 前へ: [Webの基礎](<03.0.md>)
* 次へ: [GOでwebサーバを建てる](<03.2.md>)

View File

@@ -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パッケージのつの関数を呼ぶだけで良いことがわかります。
>もしあなたが以前PHPプログラマであれば。こう問うかもしれません。我々のnginx、apacheサーバは必要ないのですかとなぜならこいつは直接tcpポートを関ししますので、nginxがやることをやってくれます。またsayhelloNameは実は我々が書いたロジック関数ですので、phpの中のコントローラcontroller関数に近いものです。
>もしあなたがpythonプログラマであったのなら、tornadoを聞いたことがあると思います。このコードはそれとよく似ていませんかええ、その通りです。goはpythonのような動的な言語によく似た特性を持っています。webアプリケーションを書くにはとても便利です。
>もしあなたがrubyプログラマであったのなら、RORの/script/serverを起動したのと少し似ている事に気づいたかもしれません。
Goを通じて簡単な数行のコードでwebサーバを立ち上げることができました。さらにこのWebサーバの内部ではマルチスレッドの特性をサポートしています。続くつの節でgoが以下にWebのマルチスレッドを実現しているのか細かくご紹介します。
## links
* [目次](<preface.md>)
* 前へ: [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パッケージのつの関数を呼ぶだけで良いことがわかります。
>もしあなたが以前PHPプログラマであれば。こう問うかもしれません。我々のnginx、apacheサーバは必要ないのですかとなぜならこいつは直接tcpポートを関ししますので、nginxがやることをやってくれます。またsayhelloNameは実は我々が書いたロジック関数ですので、phpの中のコントローラcontroller関数に近いものです。
>もしあなたがpythonプログラマであったのなら、tornadoを聞いたことがあると思います。このコードはそれとよく似ていませんかええ、その通りです。goはpythonのような動的な言語によく似た特性を持っています。webアプリケーションを書くにはとても便利です。
>もしあなたがrubyプログラマであったのなら、RORの/script/serverを起動したのと少し似ている事に気づいたかもしれません。
Goを通じて簡単な数行のコードでwebサーバを立ち上げることができました。さらにこのWebサーバの内部ではマルチスレッドの特性をサポートしています。続くつの節でgoが以下にWebのマルチスレッドを実現しているのか細かくご紹介します。
## links
* [目次](<preface.md>)
* 前へ: [Webの作業方法](<03.1.md>)
* 次へ: [Goはどのようにしてweb作業を行うか](<03.3.md>)

View File

@@ -1,9 +1,9 @@
# 3.5 概要
この章ではHTTPプロトコル、DNS名前解決のプロセス、どのようにしてgoで簡単なweb serverを実装するかご紹介しました。net/httpパッケージのソースコードに触れるうちにこのserverを実装する秘密についてお分かりいただけたかと思います。
この章の学習を通じて、GoによるWeb開発の初歩をご理解いただければ幸いです。我々はまた対応するコードを見ることでGoでWebアプリケーションを開発することがとても便利でまた相当柔軟であると分かりました。
## links
* [目次](<preface.md>)
* 前へ: [Goのhttpパッケージ詳細](<03.4.md>)
* 次へ: [フォーム](<04.0.md>)
# 3.5 概要
この章ではHTTPプロトコル、DNS名前解決のプロセス、どのようにしてgoで簡単なweb serverを実装するかご紹介しました。net/httpパッケージのソースコードに触れるうちにこのserverを実装する秘密についてお分かりいただけたかと思います。
この章の学習を通じて、GoによるWeb開発の初歩をご理解いただければ幸いです。我々はまた対応するコードを見ることでGoでWebアプリケーションを開発することがとても便利でまた相当柔軟であると分かりました。
## links
* [目次](<preface.md>)
* 前へ: [Goのhttpパッケージ詳細](<03.4.md>)
* 次へ: [フォーム](<04.0.md>)

View File

@@ -1,25 +1,25 @@
# 4 フォーム
フォームは我々が普段Webアプリケーションを書く時によく使うツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると、少々ばかり門外漢かもしれません。フォームとは一体何でしょうか?
フォームは表の要素を含むエリアです。フォームの要素はユーザがフォームの中で(例えば、テキストフィールド、コンボボックス、チェックボックス、セレクトボックス等です。)情報を入力する要素です。フォームはフォームタグ(\<form\>)で定義します。
<form>
...
input 要素
...
</form>
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
* [目次](<preface.md>)
* 前へ: [第三章概要](<03.5.md>)
* 次へ: [フォームの入力を処理する](<04.1.md>)
# 4 フォーム
フォームは我々が普段Webアプリケーションを書く時によく使うツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると、少々ばかり門外漢かもしれません。フォームとは一体何でしょうか?
フォームは表の要素を含むエリアです。フォームの要素はユーザがフォームの中で(例えば、テキストフィールド、コンボボックス、チェックボックス、セレクトボックス等です。)情報を入力する要素です。フォームはフォームタグ(\<form\>)で定義します。
<form>
...
input 要素
...
</form>
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
* [目次](<preface.md>)
* 前へ: [第三章概要](<03.5.md>)
* 次へ: [フォームの入力を処理する](<04.1.md>)

View File

@@ -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が`<script>alert()</script>`だった場合、ブラウザ上では以下のように表示されます:
![](images/4.3.escape.png?raw=true)
図4.3 Javascriptフィルターによる出力
Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。しかし時にはこの`<script>alert()</script>`を正常な情報として出力したい場合があるかもしれません。そのような場合はどのように処理するべきでしょうかこの場合はtext/templateをご利用ください。下の例をご覧ください
import "text/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
出力
Hello, <script>alert('you have been pwned')</script>!
またはtemplate.HTML型を使用すると
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))
出力
Hello, <script>alert('you have been pwned')</script>!
`template.HTML`に変換した後も、変数の内容はエスケープされません。 
エスケープの例:
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
エスケープ後の出力:
Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
## links
* [目次](<preface.md>)
* 前へ: [入力値の検証](<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が`<script>alert()</script>`だった場合、ブラウザ上では以下のように表示されます:
![](images/4.3.escape.png?raw=true)
図4.3 Javascriptフィルターによる出力
Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。しかし時にはこの`<script>alert()</script>`を正常な情報として出力したい場合があるかもしれません。そのような場合はどのように処理するべきでしょうかこの場合はtext/templateをご利用ください。下の例をご覧ください
import "text/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
出力
Hello, <script>alert('you have been pwned')</script>!
またはtemplate.HTML型を使用すると
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))
出力
Hello, <script>alert('you have been pwned')</script>!
`template.HTML`に変換した後も、変数の内容はエスケープされません。 
エスケープの例:
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
エスケープ後の出力:
Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
## links
* [目次](<preface.md>)
* 前へ: [入力値の検証](<04.2.md>)
* 次へ: [フォームの複数回送信の防止](<04.4.md>)

View File

@@ -1,58 +1,58 @@
# 4.4 フォームの複数回送信の防止
以前どこかのBBSやブログでご覧になったかもしれませんが、一つのスレや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因で、ユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか?
解決方法はフォームの中に唯一の値を持ったhiddenフィールドを追加することです。フォームを検証する際、この唯一の値を持ったフォームがすでに送信されているかどうか検証します。もしすでに送信されていれば、二回目の送信を拒絶します。そうでなければフォームに対して処理ロジックを行います。また、もしAjax形式で送信するフォームだった場合、フォームが送信された後、javascriptによってフォームの送信ボタンを禁止します。
4.2節の例を改良してみましょう:
<input type="checkbox" name="interest" value="football">サッカー
<input type="checkbox" name="interest" value="basketball">バスケットボール
<input type="checkbox" name="interest" value="tennis">テニス
ユーザ名:<input type="text" name="username">
パスワード:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="ログイン">
テンプレートの中に`token`というhiddenフィールドを追加しました。この値にはMD5(タイムスタンプ)によってユニークな値を割り当てます。この値をサーバに保存することでsessionによってコントロールは、章でどのように保存するか解説しますフォームが送信される際の判定に使うことができます。
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
* [目次](<preface.md>)
* 前へ: [クロスサイトスクリプティングの予防](<04.3.md>)
* 次へ: [ファイルのアップロード処理](<04.5.md>)
# 4.4 フォームの複数回送信の防止
以前どこかのBBSやブログでご覧になったかもしれませんが、一つのスレや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因で、ユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか?
解決方法はフォームの中に唯一の値を持ったhiddenフィールドを追加することです。フォームを検証する際、この唯一の値を持ったフォームがすでに送信されているかどうか検証します。もしすでに送信されていれば、二回目の送信を拒絶します。そうでなければフォームに対して処理ロジックを行います。また、もしAjax形式で送信するフォームだった場合、フォームが送信された後、javascriptによってフォームの送信ボタンを禁止します。
4.2節の例を改良してみましょう:
<input type="checkbox" name="interest" value="football">サッカー
<input type="checkbox" name="interest" value="basketball">バスケットボール
<input type="checkbox" name="interest" value="tennis">テニス
ユーザ名:<input type="text" name="username">
パスワード:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="ログイン">
テンプレートの中に`token`というhiddenフィールドを追加しました。この値にはMD5(タイムスタンプ)によってユニークな値を割り当てます。この値をサーバに保存することでsessionによってコントロールは、章でどのように保存するか解説しますフォームが送信される際の判定に使うことができます。
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
* [目次](<preface.md>)
* 前へ: [クロスサイトスクリプティングの予防](<04.3.md>)
* 次へ: [ファイルのアップロード処理](<04.5.md>)

View File

@@ -1,155 +1,155 @@
# 4.5 ファイルのアップロード処理
ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか
フォームにファイルをアップロードさせるためには、まずformの`enctype`属性を追加する必要があります。`enctype`属性には以下の3つの状態あります:
application/x-www-form-urlencoded 送信前にすべての文字列をエンコードする(デフォルト)
multipart/form-data 文字列に対してエンコードしません。ファイルのアップロードウィジェットを含むフォームを使用するときはこの値が必要です。
text/plain 空白を"+"記号に置き換えます。ただし、特殊文字に対してエンコードは行われません。
そのため、フォームのhtmlコードはこのようになります
<html>
<head>
<title>ファイルアップロード</title>
</head>
<body>
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
<input type="file" name="uploadfile" />
<input type="hidden" name="token" value="{{.}}"/>
<input type="submit" value="upload" />
</form>
</body>
</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
* [目次](<preface.md>)
* 前へ: [フォームの多重送信の防止](<04.4.md>)
* 次へ: [概要](<04.6.md>)
# 4.5 ファイルのアップロード処理
ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか
フォームにファイルをアップロードさせるためには、まずformの`enctype`属性を追加する必要があります。`enctype`属性には以下の3つの状態あります:
application/x-www-form-urlencoded 送信前にすべての文字列をエンコードする(デフォルト)
multipart/form-data 文字列に対してエンコードしません。ファイルのアップロードウィジェットを含むフォームを使用するときはこの値が必要です。
text/plain 空白を"+"記号に置き換えます。ただし、特殊文字に対してエンコードは行われません。
そのため、フォームのhtmlコードはこのようになります
<html>
<head>
<title>ファイルアップロード</title>
</head>
<body>
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
<input type="file" name="uploadfile" />
<input type="hidden" name="token" value="{{.}}"/>
<input type="submit" value="upload" />
</form>
</body>
</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
* [目次](<preface.md>)
* 前へ: [フォームの多重送信の防止](<04.4.md>)
* 次へ: [概要](<04.6.md>)

View File

@@ -1,9 +1,9 @@
# 4.6 概要
この一章ではGoでどのようにフォームの情報を処理するか学びました。ユーザのログインから、ファイルのアップロードの例で、Goがformの情報およびファイルをアップロードする手段についてご説明しました。しかし、フォームを処理する過程ではユーザの入力した情報を懸賞する必要があります。ホームページのセキュリティの重要性を考慮すると、データのフィルタリングは相当重要です。そのため、以降の章では異なる方面のデータフィルタリングをご説明します。同時にGoの文字列に対する正規表現についても述べます。
この一章を通してクライアントとサーバが如何にデータを互いにやりとりするか理解いただけたと思います。クライアントはデータをサーバシステムに渡し、サーバはデータを受け取って結果をクライアントにフィードバックします。
## links
* [目次](<preface.md>)
* 前へ: [ファイルのアップロードの処理](<04.5.md>)
* 次へ: [データベースへのアクセス](<05.0.md>)
# 4.6 概要
この一章ではGoでどのようにフォームの情報を処理するか学びました。ユーザのログインから、ファイルのアップロードの例で、Goがformの情報およびファイルをアップロードする手段についてご説明しました。しかし、フォームを処理する過程ではユーザの入力した情報を懸賞する必要があります。ホームページのセキュリティの重要性を考慮すると、データのフィルタリングは相当重要です。そのため、以降の章では異なる方面のデータフィルタリングをご説明します。同時にGoの文字列に対する正規表現についても述べます。
この一章を通してクライアントとサーバが如何にデータを互いにやりとりするか理解いただけたと思います。クライアントはデータをサーバシステムに渡し、サーバはデータを受け取って結果をクライアントにフィードバックします。
## links
* [目次](<preface.md>)
* 前へ: [ファイルのアップロードの処理](<04.5.md>)
* 次へ: [データベースへのアクセス](<05.0.md>)

View File

@@ -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
* [目次](<preface.md>)
* 前へ: [第四章概要](<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
* [目次](<preface.md>)
* 前へ: [第四章概要](<04.6.md>)
* 次へ: [database/sqlインターフェース](<05.1.md>)

View File

@@ -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
* [目次](<preface.md>)
* 前へ: [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
* [目次](<preface.md>)
* 前へ: [MySQLデータベースの使用](<05.2.md>)
* 次へ: [PostgreSQLデータベースの使用](<05.4.md>)

View File

@@ -1,9 +1,9 @@
# 5.7 概要
この章ではGoがどのようにdatabase/sqlインターフェースを設計するのかご説明しました。その後、サードパーティによるリレーショナルデータベースドライバの使用をご紹介しました。次にbeedbというリレーショナルデータベースに基づくORMライブラリがどのようにしてデータベースに対して簡単な操作を行うかご説明しました。最後にNOSQLのいくつかの知識をご紹介しました。現在GoのNOSQLに対するサポートはなかなかよくできています。Goは21世紀のC言語ですから、21世紀のデータベースに対するサポートも非常によくできています。
この一章の学習を通じて、いろいろなデータベースをどのように操作するか学んできました。Webの中でも重要なデータの保存に関する問題が解決したので、みなさんのdatabase/sqlの設計思想により一歩進んだ理解が深まることを願っています。
## links
* [目次](<preface.md>)
* 前へ: [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
* [目次](<preface.md>)
* 前へ: [NOSQLデータベースの操作](<05.6.md>)
* 次へ: [sessionとデータの保存](<06.0.md>)

View File

@@ -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
* [目次](<preface.md>)
* 前へ: [第五章概要](<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
* [目次](<preface.md>)
* 前へ: [第五章概要](<05.7.md>)
* 次へ: [sessionとcookie](<06.1.md>)

View File

@@ -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)