http.Handler接口
// 示例 // net/http 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并输出物品信息 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 // 实现Handler接口中定义的ServeHTTP方法 func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request){ for item, price := range db{ fmt.Fprintf(w, "%s: %s\n", item, price) } } // 带有查询字符串的请求 // /price?item=socks func (db datbase) ServeHTTP(w http.ResponseWriter, rep *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) fmt.Fprintf(w, "no such item:%q\n", item) return } fmt.Fprintf(w, "%s\n", price) default: w.WriteHeader(http.StatusNotFound) 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函数: msg := fmt.Sprintf("no such page: %s\n", req.URL) http.Error(w, msg, http.StatusNotFound) /price的case会调用URL的Query方法来将HTTP请求参数解析为一个map,或者更准确的说一个net/url包中url.Values类型的多重因隐射。然后找到第一个item参数并查找它的价格。没有找到会返回一个错误。 // 使用请求多路器ServeMux来简化URL和handers func main(){ db := database{"shoes": 50, "socks": 5} mux := http.NewServeMux() mux.Handle("/list", http.HandlerFunc(db.list)) mux.Handle("/price", http.HandleFunc(db.Price)) log.Fatal(http.ListenAndServe("localhost:8000", mux)) // 将mux作为handler } 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.Query().Get("item") price,ok := db[item] if !ok{ w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "no such item: %q\n", item) return } fmt.Fprintf(w, "%s\n", price) } 让我们关注这两个注册到handlers上的调用。第一个db.list是一个方法值,它是下面这个类型的值。 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是一个类型。它有如下定义: package http type HandlerFunc(w ReponseWriter, 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有一个方便的HandlerFunc方法,它帮我们简化handler注册代码成这样: 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值就可以工作。 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)) }
__EOF__

本文作者:404 Not Found
本文链接:https://www.cnblogs.com/weiweivip666/p/15949822.html
关于博主:可能又在睡觉
版权声明:转载请注明出处
声援博主:如果看到我睡觉请喊我去学习
本文链接:https://www.cnblogs.com/weiweivip666/p/15949822.html
关于博主:可能又在睡觉
版权声明:转载请注明出处
声援博主:如果看到我睡觉请喊我去学习
-------------------------------------------
个性签名:代码过万,键盘敲烂!!!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人