Files
2017-01-10 21:41:33 +08:00

129 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 8.3 REST
RESTful、とは現在もっとも流行しているインターネットソフトウェアフレームワークです。構造が明瞭で、標準に合っており、理解しやすく、拡張に便利です。そのため、まさに多くのホームページで採用されつつあります。この節ではこれが一体どのようなフレームワークなのか、Goではどのようにして実現するのかを学んでいきます。
## RESTとは何か
REST(Representational State Transfer)という概念は2000年Roy Thomas Fielding彼はHTTP仕様の主な著者の一人です。の博士論文の中で初めて登場しました。この中ではあるフレームワークの制約条件と原則について触れています。これらの制約条件と原則を満足したアプリケーションまたは設計はRESTfulということです。
RESTが何かを理解するためには、以下のようないくつかの概念を理解する必要があります
- リソースResources
 RESTは"プレゼンテーション層の状態遷移"です。これは主語が省略されています。"プレゼンテーション層"というのは"リソース"の"プレゼンテーション層"です。
 ではリソースとは何でしょうか普段我々がアクセスする画像、ドキュメント、映像等です。これらのリソースはURIによって特定されます。つまりひとつのURIがひとつのリソースを表しています。
- プレゼンテーション層Representation
 リソースはある具体的な実体を伴う情報を作成します。これはいくつもの表現方法を持っており、実体の表現こそがプレゼンテーション層です。例えばtxtテキスト情報があれば、これはhtml、json、xml等の形式に出力することができます。画像はjpg、png等の方法で表現することができます。これがプレゼンテーション層の意味です。
 URIはひとつのリソースを確定します。しかしどのようにこの具体的な表現形式を確定するのでしょうかHTTPリクエストのヘッダ情報においてAcceptとContent-Typeフィールドを用いて指定されているはずです。このつのフィールドこそが"プレゼンテーション層"に対する描写なのです。
- 状態遷移State Transfer
 あるホームページにアクセスすることは、クライアントがサーバとインタラクティブな過程を表しています。この過程の中では必ずデータと状態遷移が関わってきます。HTTPプロトコルはステートレスですので、これらの状態はサーバに保存されているはずです。そのため、もしクライアントがサーバにデータの変更と状態遷移を通知したい場合は、なんらかの方法によってこれを通知する必要があります。
 クライアントがサーバに通知する手段はHTTPプロトコルしかありません。具体的にはHTTPプロトコルの中にあるつの操作方法を表す動詞GET、POST、PUT、DELETEです。これらはつの基本操作に分かれますGETはリソースを取得するのに使われます。POSTはリソースを新規に作成するために使われますリソースの更新に使うこともできます。PUTは資源の更新に使われます。DELETEは資源の削除に使われます。
上の解釈を総合して、RESTfulフレームワークとは何かまとめてみます
- 1各URIがひとつのリソースを表す
- 2クライアントとサーバ間で、これらの資源の何かしらのプレゼンテーション層を転送する
- 3クライアントはつのHTTPの動詞を通して、サーバの資源に対し操作を行う。"プレゼンテーション層の状態遷移"の実現。
Webアプリケーションが満たすべきRESTの最も重要なルールはクライアントとサーバ間のやりとりにおいてリクエスト間はステートレスだということです。すなわち、クライアントからサーバへの各リクエストはすべてリクエストが必要としている情報を含んでいなければなりません。もしサーバがリクエスト間のいかなる時点で再起動しても、クライアントはその通知を受けることができません。また、このリクエストはどのような利用できるサーバからによっても回答できます。これはクラウドコンピューティングといった環境に十分適しています。ステートレスですので、クライアントはデータをキャッシュすることで性能を改善することができます。
もうひとつ重要なRESTのルールはシステムの分離です。これはモジュールがこれと直接やりとりをしているレイヤのモジュールを除いて解除することができないことを示しています。システムの知りうる内容を単一のレイヤに制限することで、システム全体の複雑さを制限することができます。そのため、低レイヤの独立性を促すことができます。
下の図はRESTのフレームワーク図です
![](images/8.3.rest2.png?raw=true)
図8.5 RESTフレームワーク図
RESTフレームワークの制約条件を全体に適用する際、大量のクライアントに向けて拡張できるアプリケーションプログラムを生成することができます。またクライアントとサーバ間のやり取りの遅延も減らします。統一されたインターフェースがシステムフレームワークの全体を簡略化し、サブシステム間のやり取りの見通しを改善します。RESTはクライアントとサーバの実装を簡略化し、RESTを使用して開発されたアプリケーションプログラムをより拡張しやすくします。
下はRESTの拡張性を示しています
![](images/8.3.rest.png?raw=true)
図8.6 RESTの拡張性
## RESTfulの実装
GoにはRESTに対する直接のサポートはありません。しかし、RESTfulなwebアプリケーションはHTTPプロトコルに基づいて実装されるものですので、`net/http`パッケージを利用することで自分で実装することができます。当然RESTに対していくつか改造を行う必要があります。RESTは異なるmethodによって対応するリソースを処理します。現在すでに存在する多くの自称RESTアプリケーションは、実は本当にRESTを実装しているわけではありません。ここではとりあえずこれらのアプリケーションを実装しているメソッドにしたがっていくつかのレベルに分けてみます、以下の図をご覧ください
![](images/8.3.rest3.png?raw=true)
図8.7 RESTのレベル分け
上の図は我々が現在実装しているRESTのつのlevelを示しています。我々がアプリケーションを開発する時も必ずしも全てのRESTfulのルールをまるっとその方式を実装しているわけではありません。なぜならある時はRESTfulの方式を完全に参照しなくても大丈夫だからです。RESTfulサービスは`DELETE``PUT`を含む各HTTPの方法を十分に利用します。しかしある時は、HTTPクライアントは`GET``POST`リクエストのみを送信できます:
- HTML標準はリンクとフォームを通してのみ`GET``POST`をサポートしています。Ajaxをサポートしていないウェブブラウザでは`PUT``DELETE`コマンドを送信することはできません。
- あるファイアウォールはHTTPの`PUT``DELETE`リクエスト遮ることがあり、この制限を迂回するにはクライアントの実際の`PUT``DELETE`リクエストをPOSTリクエストから通していかなくてはなりません。そのためRESTfulサービスは受け取ったPOSTリクエストからオリジナルのHTTPメソッドを探し出す方法と元に戻す方法を行う責任があります。
現在`POST`の中において隠されたフィールドである`_method`を増加するなどの方法で`PUT``DELETE`といったメソッドをエミュレートすることができます。しかし、サーバでは変換を行う必要があります。現在私のプロジェクトではこのような方法によってRESTインターフェースを作成しています。当然Go言語では完全にRESTfulに沿った実装を行うのは容易です。下の例を通してどのようにRESTfulなアプリケーションの設計を実現するかご説明しましょう。
```Go
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}
func getuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are get user %s", uid)
}
func modifyuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are modify user %s", uid)
}
func deleteuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are delete user %s", uid)
}
func adduser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// uid := r.FormValue("uid")
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are add user %s", uid)
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)
router.GET("/user/:uid", getuser)
router.POST("/adduser/:uid", adduser)
router.DELETE("/deluser/:uid", deleteuser)
router.PUT("/moduser/:uid", modifyuser)
log.Fatal(http.ListenAndServe(":8080", router))
}
```
上のコードではどのようにRESTなアプリケーションを書くかご覧いただきました。我々がアクセスするリソースはユーザです。異なるmethodによって異なる関数にアクセスしました。ここではサードパーティライブラリ`github.com/julienschmidt/httprouter`を使用しています。前の章でどのように自分で定義したルータを実現するかご紹介しました。このライブラリは自分で定義したルートと便利なルートのルールを反映させます。これを使って簡単にRESTのフレームワークを実装することができます。
## まとめ
RESTはフレームワークスタイルの一種です。WWWの成功経験を汲み取っていますステートレス、リソースを中心とし、HTTPプロトコルとURIプロトコルを十分利用しています。統一したインターフェース定義を提供し、Webサービスを設計する方法の一つとして流行しました。ある意味でURIとHTTPといった黎明期のInternet標準を強調することで、RESTは大型のアプリケーションプログラムサーバ時代の前のWeb方式に回帰しています。今のところGoのRESTに対するサポートはまだシンプルです。自分で定義したルーティングを通して、異なるmethodに異なるhandleを実装することができます。このようにRESTのフレームワークは実現されています。
## links
* [目次](<preface.md>)
* 前へ: [WebSocket](<08.2.md>)
* 次へ: [RPC](<08.4.md>)