<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 7.7. http.Handler接口 在第一章中,我們粗略的了解了怎么用net/http包去實現網絡客戶端(§1.5)和服務器(§1.7)。在這個小節中,我們會對那些基于http.Handler接口的服務器API做更進一步的學習: <u><i>net/http</i></u> ```go package http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) } func ListenAndServe(address string, h Handler) error ``` ListenAndServe函數需要一個例如“localhost:8000”的服務器地址,和一個所有請求都可以分派的Handler接口實例。它會一直運行,直到這個服務因為一個錯誤而失敗(或者啟動失敗),它的返回值一定是一個非空的錯誤。 想象一個電子商務網站,為了銷售,將數據庫中物品的價格映射成美元。下面這個程序可能是能想到的最簡單的實現了。它將庫存清單模型化為一個命名為database的map類型,我們給這個類型一個ServeHttp方法,這樣它可以滿足http.Handler接口。這個handler會遍歷整個map并輸出物品信息。 <u><i>gopl.io/ch7/http1</i></u> ```go func main() { db := database{"shoes": 50, "socks": 5} log.Fatal(http.ListenAndServe("localhost:8000", db)) } type dollars float32 func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) } type database map[string]dollars func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) { for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) } } ``` 如果我們啟動這個服務, ``` $ go build gopl.io/ch7/http1 $ ./http1 & ``` 然后用1.5節中的獲取程序(如果你更喜歡可以使用web瀏覽器)來連接服務器,我們得到下面的輸出: ``` $ go build gopl.io/ch1/fetch $ ./fetch http://localhost:8000 shoes: $50.00 socks: $5.00 ``` 目前為止,這個服務器不考慮URL,只能為每個請求列出它全部的庫存清單。更真實的服務器會定義多個不同的URL,每一個都會觸發一個不同的行為。讓我們使用/list來調用已經存在的這個行為并且增加另一個/price調用表明單個貨品的價格,像這樣/price?item=socks來指定一個請求參數。 <u><i>gopl.io/ch7/http2</i></u> ```go func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) { switch req.URL.Path { case "/list": for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) } case "/price": item := req.URL.Query().Get("item") price, ok := db[item] if !ok { w.WriteHeader(http.StatusNotFound) // 404 fmt.Fprintf(w, "no such item: %q\n", item) return } fmt.Fprintf(w, "%s\n", price) default: w.WriteHeader(http.StatusNotFound) // 404 fmt.Fprintf(w, "no such page: %s\n", req.URL) } } ``` 現在handler基于URL的路徑部分(req.URL.Path)來決定執行什么邏輯。如果這個handler不能識別這個路徑,它會通過調用w.WriteHeader(http.StatusNotFound)返回客戶端一個HTTP錯誤;這個檢查應該在向w寫入任何值前完成。(順便提一下,http.ResponseWriter是另一個接口。它在io.Writer上增加了發送HTTP相應頭的方法。)等效地,我們可以使用實用的http.Error函數: ```go msg := fmt.Sprintf("no such page: %s\n", req.URL) http.Error(w, msg, http.StatusNotFound) // 404 ``` /price的case會調用URL的Query方法來將HTTP請求參數解析為一個map,或者更準確地說一個net/url包中url.Values(§6.2.1)類型的多重映射。然后找到第一個item參數并查找它的價格。如果這個貨品沒有找到會返回一個錯誤。 這里是一個和新服務器會話的例子: ``` $ go build gopl.io/ch7/http2 $ go build gopl.io/ch1/fetch $ ./http2 & $ ./fetch http://localhost:8000/list shoes: $50.00 socks: $5.00 $ ./fetch http://localhost:8000/price?item=socks $5.00 $ ./fetch http://localhost:8000/price?item=shoes $50.00 $ ./fetch http://localhost:8000/price?item=hat no such item: "hat" $ ./fetch http://localhost:8000/help no such page: /help ``` 顯然我們可以繼續向ServeHTTP方法中添加case,但在一個實際的應用中,將每個case中的邏輯定義到一個分開的方法或函數中會很實用。此外,相近的URL可能需要相似的邏輯;例如幾個圖片文件可能有形如/images/\*.png的URL。因為這些原因,net/http包提供了一個請求多路器ServeMux來簡化URL和handlers的聯系。一個ServeMux將一批http.Handler聚集到一個單一的http.Handler中。再一次,我們可以看到滿足同一接口的不同類型是可替換的:web服務器將請求指派給任意的http.Handler 而不需要考慮它后面的具體類型。 對于更復雜的應用,一些ServeMux可以通過組合來處理更加錯綜復雜的路由需求。Go語言目前沒有一個權威的web框架,就像Ruby語言有Rails和python有Django。這并不是說這樣的框架不存在,而是Go語言標準庫中的構建模塊就已經非常靈活以至于這些框架都是不必要的。此外,盡管在一個項目早期使用框架是非常方便的,但是它們帶來額外的復雜度會使長期的維護更加困難。 在下面的程序中,我們創建一個ServeMux并且使用它將URL和相應處理/list和/price操作的handler聯系起來,這些操作邏輯都已經被分到不同的方法中。然后我門在調用ListenAndServe函數中使用ServeMux為主要的handler。 <u><i>gopl.io/ch7/http3</i></u> ```go func main() { db := database{"shoes": 50, "socks": 5} mux := http.NewServeMux() mux.Handle("/list", http.HandlerFunc(db.list)) mux.Handle("/price", http.HandlerFunc(db.price)) log.Fatal(http.ListenAndServe("localhost:8000", mux)) } type database map[string]dollars func (db database) list(w http.ResponseWriter, req *http.Request) { for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) } } func (db database) price(w http.ResponseWriter, req *http.Request) { item := req.URL.Query().Get("item") price, ok := db[item] if !ok { w.WriteHeader(http.StatusNotFound) // 404 fmt.Fprintf(w, "no such item: %q\n", item) return } fmt.Fprintf(w, "%s\n", price) } ``` 讓我們關注這兩個注冊到handlers上的調用。第一個db.list是一個方法值 (§6.4),它是下面這個類型的值。 ```go func(w http.ResponseWriter, req *http.Request) ``` 也就是說db.list的調用會援引一個接收者是db的database.list方法。所以db.list是一個實現了handler類似行為的函數,但是因為它沒有方法(理解:該方法沒有它自己的方法),所以它不滿足http.Handler接口并且不能直接傳給mux.Handle。 語句http.HandlerFunc(db.list)是一個轉換而非一個函數調用,因為http.HandlerFunc是一個類型。它有如下的定義: <u><i>net/http</i></u> ```go package http type HandlerFunc func(w ResponseWriter, r *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } ``` HandlerFunc顯示了在Go語言接口機制中一些不同尋常的特點。這是一個實現了接口http.Handler的方法的函數類型。ServeHTTP方法的行為是調用了它的函數本身。因此HandlerFunc是一個讓函數值滿足一個接口的適配器,這里函數和這個接口僅有的方法有相同的函數簽名。實際上,這個技巧讓一個單一的類型例如database以多種方式滿足http.Handler接口:一種通過它的list方法,一種通過它的price方法等等。 因為handler通過這種方式注冊非常普遍,ServeMux有一個方便的HandleFunc方法,它幫我們簡化handler注冊代碼成這樣: <u><i>gopl.io/ch7/http3a</i></u> ```go mux.HandleFunc("/list", db.list) mux.HandleFunc("/price", db.price) ``` 從上面的代碼很容易看出應該怎么構建一個程序:由兩個不同的web服務器監聽不同的端口,并且定義不同的URL將它們指派到不同的handler。我們只要構建另外一個ServeMux并且再調用一次ListenAndServe(可能并行的)。但是在大多數程序中,一個web服務器就足夠了。此外,在一個應用程序的多個文件中定義HTTP handler也是非常典型的,如果它們必須全部都顯式地注冊到這個應用的ServeMux實例上會比較麻煩。 所以為了方便,net/http包提供了一個全局的ServeMux實例DefaultServerMux和包級別的http.Handle和http.HandleFunc函數。現在,為了使用DefaultServeMux作為服務器的主handler,我們不需要將它傳給ListenAndServe函數;nil值就可以工作。 然后服務器的主函數可以簡化成: <u><i>gopl.io/ch7/http4</i></u> ```go func main() { db := database{"shoes": 50, "socks": 5} http.HandleFunc("/list", db.list) http.HandleFunc("/price", db.price) log.Fatal(http.ListenAndServe("localhost:8000", nil)) } ``` 最后,一個重要的提示:就像我們在1.7節中提到的,web服務器在一個新的協程中調用每一個handler,所以當handler獲取其它協程或者這個handler本身的其它請求也可以訪問到變量時,一定要使用預防措施,比如鎖機制。我們后面的兩章中將講到并發相關的知識。 **練習 7.11:** 增加額外的handler讓客戶端可以創建,讀取,更新和刪除數據庫記錄。例如,一個形如 `/update?item=socks&price=6` 的請求會更新庫存清單里一個貨品的價格并且當這個貨品不存在或價格無效時返回一個錯誤值。(注意:這個修改會引入變量同時更新的問題) **練習 7.12:** 修改/list的handler讓它把輸出打印成一個HTML的表格而不是文本。html/template包(§4.6)可能會對你有幫助。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看