新聞中心
gorilla/mux 包以直觀的 API 提供了 HTTP 請(qǐng)求路由、驗(yàn)證和其它服務(wù)。
蘆山網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,蘆山網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為蘆山1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的蘆山做網(wǎng)站的公司定做!
Go 網(wǎng)絡(luò)庫包括 http.ServeMux 結(jié)構(gòu)類型,它支持 HTTP 請(qǐng)求多路復(fù)用(路由):Web 服務(wù)器將托管資源的 HTTP 請(qǐng)求與諸如 /sales4today 之類的 URI 路由到代碼處理程序;處理程序在發(fā)送 HTTP 響應(yīng)(通常是 HTML 頁面)之前執(zhí)行適當(dāng)?shù)倪壿嫛?這是該體系的草圖:
+-----------+ +--------+ +---------+HTTP 請(qǐng)求---->| web 服務(wù)器 |---->| 路由 |---->| 處理程序 |+-----------+ +--------+ +---------+
調(diào)用 ListenAndServe 方法后啟動(dòng) HTTP 服務(wù)器:
http.ListenAndServe(":8888", nil) // args: port & router
第二個(gè)參數(shù) nil 意味著 DefaultServeMux 用于請(qǐng)求路由。
gorilla/mux 庫包含 mux.Router 類型,可替代 DefaultServeMux 或自定義請(qǐng)求多路復(fù)用器。 在 ListenAndServe 調(diào)用中,mux.Router 實(shí)例將代替 nil 作為第二個(gè)參數(shù)。 下面的示例代碼很好的說明了為什么 mux.Router如此吸引人:
1、一個(gè)簡(jiǎn)單的 CRUD web 應(yīng)用程序
crud web 應(yīng)用程序(見下文)支持四種 CRUD(創(chuàng)建/讀取/更新/刪除)操作,它們分別對(duì)應(yīng)四種 HTTP 請(qǐng)求方法:POST、GET、PUT 和 DELETE。 在這個(gè) CRUD 應(yīng)用程序中,所管理的資源是套話與反套話的列表,每個(gè)都是套話及其反面的的套話,例如這對(duì):
Out of sight, out of mind. Absence makes the heart grow fonder.
可以添加新的套話對(duì),可以編輯或刪除現(xiàn)有的套話對(duì)。
CRUD web 應(yīng)用程序:
package mainimport ("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 intCliche stringCounter string}// Message sent to goroutine that accesses the requested resource.type crudRequest struct {verb stringcp *clichePairid intcliche stringcounter stringconfirm chan string}var clichesList = []*clichePair{}var masterId = 1var crudRequests chan *crudRequest// GET /// GET /clichesfunc ClichesAll(res http.ResponseWriter, req *http.Request) {cr := &crudRequest{verb: GETALL, confirm: make(chan string)}completeRequest(cr, res, "read all")}// GET /cliches/idfunc 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 /clichesfunc ClichesCreate(res http.ResponseWriter, req *http.Request) {cliche, counter := getDataFromRequest(req)cp := new(clichePair)cp.Cliche = clichecp.Counter = countercr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}completeRequest(cr, res, "create")}// PUT /cliches/idfunc 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/idfunc 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<-crmsg := <-cr.confirmres.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 managerfor {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 listfunc addPair(cp *clichePair) string {cp.Id = masterIdmasterId++clichesList = append(clichesList, cp)return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"}// Edit an existing clichePairfunc 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 = clicheclichesList[index].Counter = countermsg = "\nCliche edited: " + cliche + " " + counter + "\n"}return msg}// Delete a clichePairfunc 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 functionsfunc 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 clichePairreq.ParseForm()form := req.Formcliche := form["cliche"][0] // 1st and only member of a listcounter := form["counter"][0] // dittoreturn 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 = masterIdmasterId++cp.Cliche = cliches[i]cp.Counter = counterCliches[i]clichesList = append(clichesList, cp)}}
為了專注于請(qǐng)求路由和驗(yàn)證,CRUD 應(yīng)用程序不使用 HTML 頁面作為請(qǐng)求響應(yīng)。 相反,請(qǐng)求會(huì)產(chǎn)生明文響應(yīng)消息:套話對(duì)的列表是對(duì) GET 請(qǐng)求的響應(yīng),確認(rèn)新的套話對(duì)已添加到列表中是對(duì) POST 請(qǐng)求的響應(yīng),依此類推。 這種簡(jiǎn)化使得使用命令行實(shí)用程序(如 curl)可以輕松地測(cè)試應(yīng)用程序,尤其是 gorilla/mux 組件。
gorilla/mux 包可以從 GitHub 安裝。 CRUD app ***期運(yùn)行;因此,應(yīng)使用 Control-C 或同等命令終止。 CRUD 應(yīng)用程序的代碼,以及自述文件和簡(jiǎn)單的 curl 測(cè)試,可以在我的網(wǎng)站上找到。
2、請(qǐng)求路由
mux.Router 擴(kuò)展了 REST 風(fēng)格的路由,它賦給 HTTP 方法(例如,GET)和 URL 末尾的 URI 或路徑(例如 /cliches)相同的權(quán)重。 URI 用作 HTTP 動(dòng)詞(方法)的名詞。 例如,在HTTP請(qǐng)求中有一個(gè)起始行,例如:
GET /cliches
意味著得到所有的套話對(duì),而一個(gè)起始線,如:
POST /cliches
意味著從 HTTP 正文中的數(shù)據(jù)創(chuàng)建一個(gè)套話對(duì)。
在 CRUD web 應(yīng)用程序中,有五個(gè)函數(shù)充當(dāng) HTTP 請(qǐng)求的五種變體的請(qǐng)求處理程序:
ClichesAll(...) # GET: 獲取所有的套話對(duì)ClichesOne(...) # GET: 獲取指定的套話對(duì)ClichesCreate(...) # POST: 創(chuàng)建新的套話對(duì)ClichesEdit(...) # PUT: 編輯現(xiàn)有的套話對(duì)ClichesDelete(...) # DELETE: 刪除指定的套話對(duì)
每個(gè)函數(shù)都有兩個(gè)參數(shù):一個(gè) http.ResponseWriter 用于向請(qǐng)求者發(fā)送一個(gè)響應(yīng),一個(gè)指向 http.Request 的指針,該指針封裝了底層 HTTP 請(qǐng)求的信息。 使用 gorilla/mux 包可以輕松地將這些請(qǐng)求處理程序注冊(cè)到Web服務(wù)器,并執(zhí)行基于正則表達(dá)式的驗(yàn)證。
CRUD 應(yīng)用程序中的 startServer 函數(shù)注冊(cè)請(qǐng)求處理程序。 考慮這對(duì)注冊(cè),router 作為 mux.Router 實(shí)例:
router.HandleFunc("/", ClichesAll).Methods("GET")router.HandleFunc("/cliches", ClichesAll).Methods("GET")
這些語句意味著對(duì)單斜線 / 或 /cliches 的 GET 請(qǐng)求應(yīng)該路由到 ClichesAll 函數(shù),然后處理請(qǐng)求。 例如,curl 請(qǐng)求(使用 % 作為命令行提示符):
% curl --request GET localhost:8888/
會(huì)產(chǎn)生如下結(jié)果:
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.
這三個(gè)套話對(duì)是 CRUD 應(yīng)用程序中的初始數(shù)據(jù)。
在這句注冊(cè)語句中:
router.HandleFunc("/cliches", ClichesAll).Methods("GET")router.HandleFunc("/cliches", ClichesCreate).Methods("POST")
URI 是相同的(/cliches),但動(dòng)詞不同:***種情況下為 GET 請(qǐng)求,第二種情況下為 POST 請(qǐng)求。 此注冊(cè)舉例說明了 REST 樣式的路由,因?yàn)閮H動(dòng)詞的不同就足以將請(qǐng)求分派給兩個(gè)不同的處理程序。
注冊(cè)中允許多個(gè) HTTP 方法,盡管這會(huì)影響 REST 風(fēng)格路由的精髓:
router.HandleFunc("/cliches", DoItAll).Methods("POST", "GET")
除了動(dòng)詞和 URI 之外,還可以在功能上路由 HTTP 請(qǐng)求。 例如,注冊(cè)
router.HandleFunc("/cliches", ClichesCreate).Schemes("https").Methods("POST")
要求對(duì) POST 請(qǐng)求進(jìn)行 HTTPS 訪問以創(chuàng)建新的套話對(duì)。以類似的方式,注冊(cè)可能需要具有指定的 HTTP 頭元素(例如,認(rèn)證憑證)的請(qǐng)求。
3、 Request validation
gorilla/mux 包采用簡(jiǎn)單,直觀的方法通過正則表達(dá)式進(jìn)行請(qǐng)求驗(yàn)證。 考慮此請(qǐng)求處理程序以獲取一個(gè)操作:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesOne).Methods("GET")
此注冊(cè)排除了 HTTP 請(qǐng)求,例如:
% curl --request GET localhost:8888/cliches/foo
因?yàn)?foo 不是十進(jìn)制數(shù)字。該請(qǐng)求導(dǎo)致熟悉的 404(未找到)狀態(tài)碼。 在此處理程序注冊(cè)中包含正則表達(dá)式模式可確保僅在請(qǐng)求 URI 以十進(jìn)制整數(shù)值結(jié)束時(shí)才調(diào)用 ClichesOne 函數(shù)來處理請(qǐng)求:
% curl --request GET localhost:8888/cliches/3 # ok
另一個(gè)例子,請(qǐng)求如下:
% curl --request PUT --data "..." localhost:8888/cliches
此請(qǐng)求導(dǎo)致狀態(tài)代碼為 405(錯(cuò)誤方法),因?yàn)?/cliches URI 在 CRUD 應(yīng)用程序中僅在 GET 和 POST 請(qǐng)求中注冊(cè)。 像 GET 請(qǐng)求一樣,PUT 請(qǐng)求必須在 URI 的末尾包含一個(gè)數(shù)字 id:
router.HandleFunc("/cliches/{id:[0-9]+}", ClichesEdit).Methods("PUT")
4、并發(fā)問題
gorilla/mux 路由器作為單獨(dú)的 Go 協(xié)程執(zhí)行對(duì)已注冊(cè)的請(qǐng)求處理程序的每次調(diào)用,這意味著并發(fā)性被內(nèi)置于包中。 例如,如果有十個(gè)同時(shí)發(fā)出的請(qǐng)求,例如
% curl --request POST --data "..." localhost:8888/cliches
然后 mux.Router 啟動(dòng)十個(gè) Go 協(xié)程來執(zhí)行 ClichesCreate 處理程序。
GET all、GET one、POST、PUT 和 DELETE 中的五個(gè)請(qǐng)求操作中,***三個(gè)改變了所請(qǐng)求的資源,即包含套話對(duì)的共享 clichesList。 因此,CRUD app 需要通過協(xié)調(diào)對(duì) clichesList 的訪問來保證安全的并發(fā)性。 在不同但等效的術(shù)語中,CRUD app 必須防止 clichesList 上的競(jìng)爭(zhēng)條件。 在生產(chǎn)環(huán)境中,可以使用數(shù)據(jù)庫系統(tǒng)來存儲(chǔ)諸如 clichesList 之類的資源,然后可以通過數(shù)據(jù)庫事務(wù)來管理安全并發(fā)。
CRUD 應(yīng)用程序采用推薦的Go方法來實(shí)現(xiàn)安全并發(fā):
- 只有一個(gè) Go 協(xié)程,資源管理器在 CRUD app
startServer函數(shù)中啟動(dòng),一旦 Web 服務(wù)器開始偵聽請(qǐng)求,就可以訪問clichesList。 - 諸如
ClichesCreate和ClichesAll之類的請(qǐng)求處理程序向 Go 通道發(fā)送(指向)crudRequest實(shí)例(默認(rèn)情況下是線程安全的),并且資源管理器單獨(dú)從該通道讀取。 然后,資源管理器對(duì)clichesList執(zhí)行請(qǐng)求的操作。
安全并發(fā)體系結(jié)構(gòu)繪制如下:
crudRequest 讀/寫請(qǐng)求處理程序 -------------> 資源托管者 ------------> 套話列表
在這種架構(gòu)中,不需要顯式鎖定 clichesList,因?yàn)橐坏?CRUD 請(qǐng)求開始進(jìn)入,只有一個(gè) Go 協(xié)程(資源管理器)訪問 clichesList。
為了使 CRUD 應(yīng)用程序盡可能保持并發(fā),在一方請(qǐng)求處理程序與另一方的單一資源管理器之間進(jìn)行有效的分工至關(guān)重要。 在這里,為了審查,是 ClichesCreate 請(qǐng)求處理程序:
func ClichesCreate(res http.ResponseWriter, req *http.Request) {cliche, counter := getDataFromRequest(req)cp := new(clichePair)cp.Cliche = clichecp.Counter = countercr := &crudRequest{verb: POST, cp: cp, confirm: make(chan string)}completeRequest(cr, res, "create")}
ClichesCreate 調(diào)用實(shí)用函數(shù) getDataFromRequest,它從 POST 請(qǐng)求中提取新的套話和反套話。 然后 ClichesCreate 函數(shù)創(chuàng)建一個(gè)新的 ClichePair,設(shè)置兩個(gè)字段,并創(chuàng)建一個(gè) crudRequest 發(fā)送給單個(gè)資源管理器。 此請(qǐng)求包括一個(gè)確認(rèn)通道,資源管理器使用該通道將信息返回給請(qǐng)求處理程序。 所有設(shè)置工作都可以在不涉及資源管理器的情況下完成,因?yàn)樯形丛L問 clichesList。
請(qǐng)求處理程序調(diào)用實(shí)用程序函數(shù),該函數(shù)從 POST 請(qǐng)求中提取新的套話和反套話。 然后,該函數(shù)創(chuàng)建一個(gè)新的,設(shè)置兩個(gè)字段,并創(chuàng)建一個(gè) crudRequest 發(fā)送到單個(gè)資源管理器。 此請(qǐng)求包括一個(gè)確認(rèn)通道,資源管理器使用該通道將信息返回給請(qǐng)求處理程序。 所有設(shè)置工作都可以在不涉及資源管理器的情況下完成,因?yàn)樯形丛L問它。
completeRequest 實(shí)用程序函數(shù)在 ClichesCreate 函數(shù)和其他請(qǐng)求處理程序的末尾調(diào)用:
completeRequest(cr, res, "create") // shown above
通過將 crudRequest 放入 crudRequests 頻道,使資源管理器發(fā)揮作用:
func completeRequest(cr *crudRequest, res http.ResponseWriter, logMsg string) {crudRequests<-cr // 向資源托管者發(fā)送請(qǐng)求msg := <-cr.confirm // 等待確認(rèn)res.Write([]byte(msg)) // 向請(qǐng)求方發(fā)送確認(rèn)logIt(logMsg) // 打印到標(biāo)準(zhǔn)輸出}
對(duì)于 POST 請(qǐng)求,資源管理器調(diào)用實(shí)用程序函數(shù) addPair,它會(huì)更改 clichesList 資源:
func addPair(cp *clichePair) string {cp.Id = masterId // 分配一個(gè)唯一的 IDmasterId++ // 更新 ID 計(jì)數(shù)器clichesList = append(clichesList, cp) // 更新列表return "\nCreated: " + cp.Cliche + " " + cp.Counter + "\n"}
資源管理器為其他 CRUD 操作調(diào)用類似的實(shí)用程序函數(shù)。 值得重復(fù)的是,一旦 Web 服務(wù)器開始接受請(qǐng)求,資源管理器就是唯一可以讀取或?qū)懭?clichesList 的 goroutine。
對(duì)于任何類型的 Web 應(yīng)用程序,gorilla/mux 包在簡(jiǎn)單直觀的 API 中提供請(qǐng)求路由、請(qǐng)求驗(yàn)證和相關(guān)服務(wù)。 CRUD web 應(yīng)用程序突出了軟件包的主要功能。
新聞名稱:使用gorilla/mux進(jìn)行HTTP請(qǐng)求路由和驗(yàn)證
轉(zhuǎn)載來于:http://www.dlmjj.cn/article/dpsspos.html


咨詢
建站咨詢

