Goネットワークライブラリには、http.ServeMux
が含まれています HTTPリクエストの多重化(ルーティング)をサポートする構造タイプ:ウェブサーバーは、 / sales4today などのURIを使用して、ホストされたリソースのHTTPリクエストをルーティングします。 、コードハンドラーへ。ハンドラーは、HTTP応答(通常はHTMLページ)を送信する前に適切なロジックを実行します。アーキテクチャのスケッチは次のとおりです。
+------------+ +--------+ +---------+
HTTP request---->| web server |---->| router |---->| handler |
+------------+ +--------+ +---------+
ListenAndServe
の呼び出しで HTTPサーバーを起動する方法
http.ListenAndServe(":8888", nil) // args: port & router
nil
の2番目の引数 DefaultServeMux
を意味します リクエストルーティングに使用されます。
gorilla/mux
パッケージにはmux.Router
があります DefaultServeMux
の代わりに入力します またはカスタマイズされた要求マルチプレクサ。 ListenAndServe
で 呼び出し、mux.Router
インスタンスはnil
を置き換えます 2番目の引数として。 mux.Router
の特徴 そのため、魅力的なものはコード例で最もよく示されます:
1。 CrudWebアプリのサンプル
crud Webアプリケーション(以下を参照)は、4つのCRUD(Create Read Update Delete)操作をサポートします。これらの操作は、それぞれPOST、GET、PUT、およびDELETEの4つのHTTP要求メソッドに一致します。 crud アプリの場合、ホストされるリソースは決まり文句のペアのリストであり、それぞれが決まり文句と、このペアなどの競合する決まり文句です。
Out of sight, out of mind. Absence makes the heart grow fonder.
新しいクリシェペアを追加したり、既存のクリシェペアを編集または削除したりできます。
crud ウェブアプリ
package main
import (
"gorilla/mux"
"net/http"
"fmt"
"strconv"
)
const GETALL string = "GETALL"
const GETONE string = "GETONE"
const POST string = "POST"
const PUT string = "PUT"
const DELETE string = "DELETE"
type clichePair struct {
Id int
Cliche string
Counter string
}
// Message sent to goroutine that accesses the requested resource.
type crudRequest struct {
verb string
cp *clichePair
id int
cliche string
counter string
confirm chan string
}
var clichesList = []*clichePair{}
var masterId = 1
var crudRequests chan *crudRequest
// GET /
// GET /cliches
func ClichesAll(res http.ResponseWriter, req *http.Request) {
cr := &crudRequest{verb: GETALL, confirm: make(chan string)}
completeRequest(cr, res, "read all")
}
// GET /cliches/id
func ClichesOne(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cr := &crudRequest{verb: GETONE, id: id, confirm: make(chan string)}
completeRequest(cr, res, "read one")
}
// POST /cliches
func ClichesCreate(res http.ResponseWriter, req *http.Request) {
cliche, counter := getDataFromRequest(req)
cp := new(clichePair)
cp.Cliche = cliche
cp.Counter = counter
cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
completeRequest(cr, res, "create")
}
// PUT /cliches/id
func ClichesEdit(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cliche, counter := getDataFromRequest(req)
cr := &crudRequest{verb: PUT, id: id, cliche: cliche, counter: counter, confirm: make(chan string)}
completeRequest(cr, res, "edit")
}
// DELETE /cliches/id
func ClichesDelete(res http.ResponseWriter, req *http.Request) {
id := getIdFromRequest(req)
cr := &crudRequest{verb: DELETE, id: id, confirm: make(chan string)}
completeRequest(cr, res, "delete")
}
func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
crudRequests<-cr
msg := <-cr.confirm
res.Write([]byte(msg))
logIt(logMsg)
}
func main() {
populateClichesList()
// From now on, this gorountine alone accesses the clichesList.
crudRequests = make(chan *crudRequest, 8)
go func() { // resource manager
for {
select {
case req := <-crudRequests:
if req.verb == GETALL {
req.confirm<-readAll()
} else if req.verb == GETONE {
req.confirm<-readOne(req.id)
} else if req.verb == POST {
req.confirm<-addPair(req.cp)
} else if req.verb == PUT {
req.confirm<-editPair(req.id, req.cliche, req.counter)
} else if req.verb == DELETE {
req.confirm<-deletePair(req.id)
}
}
}()
startServer()
}
func startServer() {
router := mux.NewRouter()
// Dispatch map for CRUD operations.
router.HandleFunc("/", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")
router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesDelete).Methods("DELETE")
http.Handle("/", router) // enable the router
// Start the server.
port := ":8888"
fmt.Println("\nListening on port " + port)
http.ListenAndServe(port, router); // mux.Router now in play
}
// Return entire list to requester.
func readAll() string {
msg := "\n"
for _, cliche := range clichesList {
next := strconv.Itoa(cliche.Id) + ": " + cliche.Cliche + " " + cliche.Counter + "\n"
msg += next
}
return msg
}
// Return specified clichePair to requester.
func readOne(id int) string {
msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"
index := findCliche(id)
if index >= 0 {
cliche := clichesList[index]
msg = "\n" + strconv.Itoa(id) + ": " + cliche.Cliche + " " + cliche.Counter + "\n"
}
return msg
}
// Create a new clichePair and add to list
func addPair(cp *clichePair) string {
cp.Id = masterId
masterId++
clichesList = append(clichesList, cp)
return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}
// Edit an existing clichePair
func editPair(id int, cliche string, counter string) string {
msg := "\n" + "Bad Id: " + strconv.Itoa(id) + "\n"
index := findCliche(id)
if index >= 0 {
clichesList[index].Cliche = cliche
clichesList[index].Counter = counter
msg = "\nCliche edited: " + cliche + " " + counter + "\n"
}
return msg
}
// Delete a clichePair
func deletePair(id int) string {
idStr := strconv.Itoa(id)
msg := "\n" + "Bad Id: " + idStr + "\n"
index := findCliche(id)
if index >= 0 {
clichesList = append(clichesList[:index], clichesList[index + 1:]...)
msg = "\nCliche " + idStr + " deleted\n"
}
return msg
}
//*** utility functions
func findCliche(id int) int {
for i := 0; i < len(clichesList); i++ {
if id == clichesList[i].Id {
return i;
}
}
return -1 // not found
}
func getIdFromRequest(req *http.Request) int {
vars := mux.Vars(req)
id, _ := strconv.Atoi(vars["id"])
return id
}
func getDataFromRequest(req *http.Request) (string, string) {
// Extract the user-provided data for the new clichePair
req.ParseForm()
form := req.Form
cliche := form["cliche"][0] // 1st and only member of a list
counter := form["counter"][0] // ditto
return cliche, counter
}
func logIt(msg string) {
fmt.Println(msg)
}
func populateClichesList() {
var cliches = []string {
"Out of sight, out of mind.",
"A penny saved is a penny earned.",
"He who hesitates is lost.",
}
var counterCliches = []string {
"Absence makes the heart grow fonder.",
"Penny-wise and dollar-foolish.",
"Look before you leap.",
}
for i := 0; i < len(cliches); i++ {
cp := new(clichePair)
cp.Id = masterId
masterId++
cp.Cliche = cliches[i]
cp.Counter = counterCliches[i]
clichesList = append(clichesList, cp)
}
}
リクエストのルーティングと検証に焦点を当てるために、 crud アプリは、リクエストへの応答としてHTMLページを使用しません。代わりに、リクエストはプレーンテキストの応答メッセージになります。決まり文句のペアのリストはGETリクエストへの応答であり、新しい決まり文句のペアがリストに追加されたことの確認はPOSTリクエストへの応答です。この簡略化により、アプリ、特にgorilla/mux
のテストが簡単になります。 curlなどのコマンドラインユーティリティを備えたコンポーネント 。
gorilla/mux
パッケージはGitHubからインストールできます。 crud アプリは無期限に実行されます。したがって、Control-Cまたは同等のもので終了する必要があります。 crudのコード アプリ、READMEおよびサンプル curl テストは、私のWebサイトで入手できます。
2。ルーティングをリクエストする
mux.Router
RESTスタイルのルーティングを拡張します。これにより、HTTPメソッド(例:GET)とURLの末尾のURIまたはパス(例: / cliches )に同等の重みが与えられます。 )。 URIは、HTTP動詞(メソッド)の名詞として機能します。たとえば、HTTPリクエストでは、
GET /cliches
すべての決まり文句のペアを取得する 、一方、
などのスタートラインPOST /cliches
HTTPボディのデータから決まり文句のペアを作成することを意味します 。
crud Webアプリには、HTTPリクエストの5つのバリエーションのリクエストハンドラーとして機能する5つの関数があります。
ClichesAll(...) # GET: get all of the cliche pairs
ClichesOne(...) # GET: get a specified cliche pair
ClichesCreate(...) # POST: create a new cliche pair
ClichesEdit(...) # PUT: edit an existing cliche pair
ClichesDelete(...) # DELETE: delete a specified cliche pair
各関数は2つの引数を取ります:http.ResponseWriter
リクエスターに応答を送り返すため、およびhttp.Request
へのポインター 、基になるHTTPリクエストからの情報をカプセル化します。 gorilla/mux
パッケージを使用すると、これらのリクエストハンドラをウェブサーバーに簡単に登録し、正規表現ベースの検証を実行できます。
startServer
crudで機能する appはリクエストハンドラーを登録します。 router
を使用したこの登録のペアについて考えてみます。 mux.Router
として インスタンス:
router.HandleFunc("/", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
これらのステートメントは、単一のスラッシュ /のいずれかに対するGETリクエストを意味します。 または/cliches ClichesAll
にルーティングする必要があります 次に、リクエストを処理する関数。たとえば、 curl リクエスト(コマンドラインプロンプトとして%を使用)
% curl --request GET localhost:8888/
この応答を生成します:
1: Out of sight, out of mind. Absence makes the heart grow fonder.
2: A penny saved is a penny earned. Penny-wise and dollar-foolish.
3: He who hesitates is lost. Look before you leap.
3つの決まり文句のペアは、 crudの初期データです。 アプリ。
この登録届出書のペアでは
router.HandleFunc("/cliches", ClichesAll).Methods("GET")
router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
URIは同じです( / cliches )ただし、動詞は異なります。最初のケースではGET、2番目のケースではPOSTです。この登録は、動詞の違いだけで2つの異なるハンドラーにリクエストをディスパッチするのに十分であるため、RESTスタイルのルーティングを例示しています。
1つの登録で複数のHTTPメソッドを使用できますが、これはRESTスタイルのルーティングの精神を損ないます:
router.HandleFunc("/cliches", DoItAll).Methods("POST", "GET")
HTTPリクエストは、動詞とURI以外の機能にルーティングできます。たとえば、登録
router.HandleFunc("/cliches", ClichesCreate).Schemes("https").Methods("POST")
新しいclicheペアを作成するには、POSTリクエストにHTTPSアクセスが必要です。同様に、登録では、指定されたHTTPヘッダー要素(認証クレデンシャルなど)を持つリクエストが必要になる場合があります。
3。検証をリクエストする
gorilla/mux
packageは、正規表現を介して検証を要求するための簡単で直感的なアプローチを採用しています。 get oneのこのリクエストハンドラを検討してください 操作:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")
この登録により、
などのHTTPリクエストが除外されます。% curl --request GET localhost:8888/cliches/foo
foo 10進数ではありません。リクエストの結果、おなじみの404(見つかりません)ステータスコードが表示されます。このハンドラー登録に正規表現パターンを含めると、ClichesOne
が確実になります。 関数は、リクエストURIが10進整数値で終わる場合にのみリクエストを処理するために呼び出されます:
% curl --request GET localhost:8888/cliches/3 # ok
2番目の例として、リクエストについて考えてみましょう
% curl --request PUT --data "..." localhost:8888/cliches
/ cliches が原因で、このリクエストのステータスコードは405(不正な方法)になります。 URIはcrudに登録されています アプリ、GETおよびPOSTリクエストのみ。 PUTリクエストは、GET oneリクエストのように、URIの最後に数値IDを含める必要があります:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
4。並行性の問題
gorilla/mux
ルーターは、登録されたリクエストハンドラーへの各呼び出しを個別のゴルーチンとして実行します。これは、同時実行性がパッケージに組み込まれていることを意味します。たとえば、
% curl --request POST --data "..." localhost:8888/cliches
次に、mux.Router
ClichesCreate
を実行するために10個のゴルーチンを起動します ハンドラー。
5つのリクエスト操作GETall、GET one、POST、PUT、およびDELETEのうち、最後の3つは、リクエストされたリソースである共有clichesList
を変更します。 決まり文句のペアを収容します。したがって、 crud アプリは、clichesList
へのアクセスを調整することにより、安全な同時実行性を保証する必要があります 。異なるが同等の用語で、 crud アプリはclichesList
の競合状態を防ぐ必要があります 。実稼働環境では、データベースシステムを使用して、clichesList
などのリソースを格納できます。 、そして安全な同時実行性は、データベーストランザクションを通じて管理できます。
crud アプリは、安全な同時実行性のために推奨されるGoアプローチを採用しています:
- 単一のゴルーチン、リソースマネージャー crudで開始 アプリ
startServer
関数、clichesList
にアクセスできます Webサーバーがリクエストのリッスンを開始したら。 -
ClichesCreate
などのリクエストハンドラー およびClichesAll
crudRequest
への(ポインタ)を送信します インスタンスをGoチャネル(デフォルトではスレッドセーフ)に切り替え、リソースマネージャーのみがこのチャネルから読み取ります。次に、リソースマネージャーは、clichesList
で要求された操作を実行します。 。
安全な同時実行アーキテクチャは、次のようにスケッチできます。
crudRequest read/write
request handlers------------->resource manager------------>clichesList
このアーキテクチャでは、clichesList
を明示的にロックする必要はありません。 clichesList
にアクセスするのはリソースマネージャーの1つのゴルーチンだけなので、必要です。 CRUDリクエストが届き始めたら。
crudを維持するには アプリを可能な限り同時に使用するには、一方のリクエストハンドラーと、もう一方の単一のリソースマネージャーの間で効率的な分業を行うことが不可欠です。ここで、レビューのために、ClichesCreate
があります リクエストハンドラ:
func ClichesCreate(res http.ResponseWriter, req *http.Request) {
cliche, counter := getDataFromRequest(req)
cp := new(clichePair)
cp.Cliche = cliche
cp.Counter = counter
cr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}
completeRequest(cr, res, "create")
}
その他のLinuxリソース
- Linuxコマンドのチートシート
- 高度なLinuxコマンドのチートシート
- 無料のオンラインコース:RHELの技術概要
- Linuxネットワーキングのチートシート
- SELinuxチートシート
- Linuxの一般的なコマンドのチートシート
- Linuxコンテナとは何ですか?
- 最新のLinux記事
リクエストハンドラーClichesCreate
ユーティリティ関数getDataFromRequest
を呼び出します 、POSTリクエストから新しい決まり文句と反決まり文句を抽出します。 ClichesCreate
次に、関数は新しいClichePair
を作成します 、2つのフィールドを設定し、crudRequest
を作成します 単一のリソースマネージャーに送信されます。この要求には、リソース・マネージャーが情報を要求ハンドラーに返すために使用する確認チャネルが含まれています。 clichesList
により、リソースマネージャーを使用せずにすべてのセットアップ作業を実行できます。 まだアクセスされていません。
completeRequest
ClichesCreate
の最後に呼び出されるユーティリティ関数 関数とその他のリクエストハンドラー
completeRequest(cr, res, "create") // shown above
crudRequest
を配置することにより、リソースマネージャーを機能させます crudRequests
に チャネル:
func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {
crudRequests<-cr // send request to resource manager
msg := <-cr.confirm // await confirmation string
res.Write([]byte(msg)) // send confirmation back to requester
logIt(logMsg) // print to the standard output
}
POSTリクエストの場合、リソースマネージャーはユーティリティ関数addPair
を呼び出します。 、clichesList
を変更します リソース:
func addPair(cp *clichePair) string {
cp.Id = masterId // assign a unique ID
masterId++ // update the ID counter
clichesList = append(clichesList, cp) // update the list
return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"
}
リソースマネージャーは、他のCRUD操作に対して同様のユーティリティ関数を呼び出します。 clichesList
を読み書きするための唯一のゴルーチンは、リソースマネージャーであることを繰り返す価値があります。 Webサーバーがリクエストの受け入れを開始したら。
あらゆるタイプのWebアプリケーションの場合、gorilla/mux
パッケージは、リクエストのルーティング、リクエストの検証、および関連サービスをわかりやすく直感的なAPIで提供します。 crud ウェブアプリは、パッケージの主な機能を強調しています。パッケージを試してみると、購入者になる可能性があります。