回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,6 +1,6 @@
## 7.7. http.Handler接口
在第一章中,我粗略的了解了怎用net/http包去實現網絡客戶端(§1.5)和服器(§1.7)。在這個小節中,我們會對那些基http.Handler接口的服器API做更一步的學習
在第一章中,我粗略的了解了怎用net/http包去实现网络客户端(§1.5)和服器(§1.7)。在这个小节中,我们会对那些基http.Handler接口的服器API做更一步的学习
<u><i>net/http</i></u>
```go
@@ -13,9 +13,9 @@ type Handler interface {
func ListenAndServe(address string, h Handler) error
```
ListenAndServe函需要一例如“localhost:8000”的服器地址,和一所有求都可以分派的Handler接口例。它一直行,直到這個服務因爲一個錯誤而失(或者啟動失敗),它的返值一定是一非空的錯誤
ListenAndServe函需要一例如“localhost:8000”的服器地址,和一所有求都可以分派的Handler接口例。它一直行,直到这个服务因为一个错误而失(或者启动失败),它的返值一定是一非空的错误
想象一個電子商務網站,爲了銷售它的數據庫將它物品的格映射成美元。下面這個程序可能是能想到的最簡單的實現了。它將庫存清模型化爲一個命名database的map型,我們給這個類型一ServeHttp方法這樣它可以滿足http.Handler接口。這個handler會遍歷整個map併輸出物品信息。
想象一个电子商务网站,为了销售它的数据库将它物品的格映射成美元。下面这个程序可能是能想到的最简单的实现了。它将库存清模型化为一个命名database的map型,我们给这个类型一ServeHttp方法这样它可以足http.Handler接口。这个handler会遍历整个map并输出物品信息。
<u><i>gopl.io/ch7/http1</i></u>
```go
@@ -37,14 +37,14 @@ func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
```
如果我們啟動這個服務
如果我们启动这个服务
```
$ go build gopl.io/ch7/http1
$ ./http1 &
```
用1.5中的取程序(如果你更喜可以使用web瀏覽器)來連接服器,我得到下面的出:
用1.5中的取程序(如果你更喜可以使用web浏览器)来连接服器,我得到下面的出:
```
$ go build gopl.io/ch1/fetch
@@ -53,7 +53,7 @@ shoes: $50.00
socks: $5.00
```
目前止,這個服務器不考URL隻能爲每個請求列出它全部的存清。更眞實的服務器會定義多個不同的URL每一個都會觸發一個不同的行爲。讓我們使用/list來調用已存在的這個行爲併且增加另一/price調用表明單個貨品的格,像這樣/price?item=socks指定一個請求參數
目前止,这个服务器不考URL只能为每个请求列出它全部的存清。更真实的服务器会定义多个不同的URL每一个都会触发一个不同的行为。让我们使用/list来调用已存在的这个行为并且增加另一/price用表明单个货品的格,像这样/price?item=socks指定一个请求参数
<u><i>gopl.io/ch7/http2</i></u>
```go
@@ -79,16 +79,16 @@ func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
```
在handler基URL的路部分req.URL.Path來決定執行什麽邏輯。如果這個handler不能識别這個路徑,它會通過調用w.WriteHeader(http.StatusNotFound)返迴客戶端一HTTP錯誤;這個檢査應該在向w入任何值前完成。(便提一下http.ResponseWriter是另一接口。它在io.Writer上增加了送HTTP相應頭的方法。)等效地,我可以使用用的http.Error函
在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參數併査找它的格。如果這個貨品沒有找到會返迴一個錯誤
/price的case会调用URL的Query方法来将HTTP请求参数解析为一个map或者更准确地说一个net/url包中url.Values(§6.2.1)型的多重映射。然找到第一item参数并查找它的格。如果这个货品没有找到会返回一个错误
里是一和新服務器會話的例子:
里是一和新服务器会话的例子:
```
$ go build gopl.io/ch7/http2
@@ -107,12 +107,12 @@ $ ./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
而不需要考慮它後面的具體類型。
然我可以继续向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可以通过组合来处理更加错综复杂的路由需求。Go言目前有一个权威的web框架就像Ruby言有Rails和python有Django。这并不是说这样的框架不存在而是Go语言标准库中的建模就已非常活以至于这些框架都是不必要的。此外,管在一个项目早期使用框架是非常方便的,但是它们带来额外的复杂度会使长期的维护更加困难
在下面的程序中,我們創建一ServeMux且使用它URL和相應處理/list和/price操作的handler聯繫起來,這些操作邏輯都已被分到不同的方法中。然後我門在調用ListenAndServe函中使用ServeMux最主要的handler。
在下面的程序中,我们创建一ServeMux且使用它URL和相应处理/list和/price操作的handler联系起来,这些操作逻辑都已被分到不同的方法中。然后我门在调用ListenAndServe函中使用ServeMux最主要的handler。
<u><i>gopl.io/ch7/http3</i></u>
```go
@@ -144,15 +144,15 @@ func (db database) price(w http.ResponseWriter, req *http.Request) {
}
```
讓我們關註這兩個註冊到handlers上的調用。第一db.list是一方法值 (§6.4),它是下面這個類型的值
让我们关注这两个注册到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。
也就是db.list的调用会援引一接收者是db的database.list方法。所以db.list是一个实现了handler似行的函,但是因为它没有方法,所以它不足http.Handler接口且不能直接传给mux.Handle。
句http.HandlerFunc(db.list)是一個轉換而非一個函數調用,因http.HandlerFunc是一個類型。它有如下的定
句http.HandlerFunc(db.list)是一个转换而非一个函数调用,因http.HandlerFunc是一个类型。它有如下的定
<u><i>net/http</i></u>
```go
@@ -165,9 +165,9 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
}
```
HandlerFunc示了在Go言接口機製中一些不同常的特點。這是一個有實現了接口http.Handler方法的函數類型。ServeHTTP方法的行爲調用了它本身的函。因此HandlerFunc是一個讓函數值滿足一接口的配器,里函數和這個接口有的方法有相同的函數籤名。實際上,這個技巧讓一個單一的型例如database以多方式滿足http.Handler接口種通過它的list方法種通過它的price方法等等。
HandlerFunc示了在Go言接口机制中一些不同常的特点。这是一个有实现了接口http.Handler方法的函数类型。ServeHTTP方法的行为调用了它本身的函。因此HandlerFunc是一个让函数值满足一接口的配器,里函数和这个接口有的方法有相同的函数签名。实际上,这个技巧让一个单一的型例如database以多方式足http.Handler接口种通过它的list方法种通过它的price方法等等。
handler通過這種方式註冊非常普遍ServeMux有一方便的HandleFunc方法幫我們簡化handler註冊代碼成這樣
handler通过这种方式注册非常普遍ServeMux有一方便的HandleFunc方法帮我们简化handler注册代码成这样
<u><i>gopl.io/ch7/http3a</i></u>
```go
@@ -175,11 +175,11 @@ mux.HandleFunc("/list", db.list)
mux.HandleFunc("/price", db.price)
```
上面的代很容易看出應該怎麽構建一程序,它有兩個不同的web服務器監聽不同的端口的,且定不同的URL將它們指派到不同的handler。我們隻要構建另外一ServeMux且在調用一次ListenAndServe可能行的)。但是在大多程序中,一web服器就足了。此外,在一個應用程序的多文件中定HTTP handler也是非常典型的如果它們必須全部都示的註冊到這個應用的ServeMux例上會比較麻煩
上面的代很容易看出应该怎么构建一程序,它有两个不同的web服务器监听不同的端口的,且定不同的URL将它们指派到不同的handler。我们只要构建另外一ServeMux且在用一次ListenAndServe可能行的)。但是在大多程序中,一web服器就足了。此外,在一个应用程序的多文件中定HTTP handler也是非常典型的如果它们必须全部都示的注册到这个应用的ServeMux例上会比较麻烦
所以了方便net/http包提供了一全局的ServeMux例DefaultServerMux和包别的http.Handle和http.HandleFunc函數。現在,了使用DefaultServeMux作爲服務器的主handler不需要將它傳給ListenAndServe函nil值就可以工作。
所以了方便net/http包提供了一全局的ServeMux例DefaultServerMux和包别的http.Handle和http.HandleFunc函数。现在,了使用DefaultServeMux作为服务器的主handler不需要将它传给ListenAndServe函nil值就可以工作。
後服務器的主函可以化成:
后服务器的主函可以化成:
<u><i>gopl.io/ch7/http4</i></u>
```go
@@ -191,8 +191,8 @@ func main() {
}
```
,一重要的提示:就像我在1.7中提到的web服器在一新的程中調用每一handler所以handler取其它程或者這個handler本身的其它求也可以訪問的變量時一定要使用防措施比如鎖機製。我們後面的章中講到併發相關的知
,一重要的提示:就像我在1.7中提到的web服器在一新的程中用每一handler所以handler取其它程或者这个handler本身的其它求也可以访问的变量时一定要使用防措施比如锁机制。我们后面的章中讲到并发相关的知
**練習 7.11** 增加外的handler客服端可以建,取,更新和刪除數據庫記録。例如,一形如 `/update?item=socks&price=6`請求會更新存清里一個貨品的價格併且當這個貨品不存在或價格無效時返迴一個錯誤值。(意:這個脩改會引入量同更新的問題
**练习 7.11** 增加外的handler客服端可以建,取,更新和删除数据库记录。例如,一形如 `/update?item=socks&price=6`请求会更新存清里一个货品的价格并且当这个货品不存在或价格无效时返回一个错误值。(意:这个修改会引入量同更新的问题
**練習 7.12** 改/list的handler它把出打印成一HTML的表格而不是文本。html/template包(§4.6)可能會對你有助。
**练习 7.12** 改/list的handler它把出打印成一HTML的表格而不是文本。html/template包(§4.6)可能会对你有助。