go http handle
Golang构建HTTP服务(二)--- Handler,ServeMux与中间件 - 简书
打印所有的Header
func helloFunc(w http.ResponseWriter, r *http.Request) {
fmt.Println("打印Header参数列表:")
if len(r.Header) > 0 {
for k,v := range r.Header {
fmt.Printf("%s=%s\n", k, v[0])
}
}
fmt.Println("打印Form参数列表:")
r.ParseForm()
if len(r.Form) > 0 {
for k,v := range r.Form {
fmt.Printf("%s=%s\n", k, v[0])
}
}
//验证用户名密码,如果成功则header里返回session,失败则返回StatusUnauthorized状态码
w.WriteHeader(http.StatusOK)
if (r.Form.Get("user") == "admin") && (r.Form.Get("pass") == "888") {
w.Write([]byte("hello,验证成功!"))
} else {
w.Write([]byte("hello,验证失败了!"))
}
}
1. http.Handle 默认方法,参数为接口,
对于一个自定义的func hello (w http.ResponseWriter, r *http.Request)
是不能直接直接传参的,但是http.HandlerFunc(fn)转换一下就可以了.
HandlerFunc(fn) 转换
func Handle(pattern string, handler Handler) {
DefaultServeMux.Handle(pattern, handler)
}
2. http.HandleFunc 默认方法,参数为 http.HandlerFunc 函数
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
3. http.Handler 接口
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
4. http.HandlerFunc是一个签名, 只是这个签名和handler接口的ServeHTTP()一样
并且实现 http.Handler 接口,用来自己调用自己方法
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Middleware 中间件的定义
type Middleware func(http.Handler) http.Handler
type Middlewares []func(http.Handler) http.Handler
原文地址 https://www.jianshu.com/p/3b5c4fc0695c
例子
先看一个简单的例子:
package main
import (
"fmt"
"net/http"
)
type HelloHandler struct{}
实现handler接口
func (h HelloHandler) ServeHTTP (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello Handler!")
}
func hello (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
helloHandler := HelloHandler{}
http.Handle("/hello1", helloHandler)
http.HandleFunc("/hello2", hello)
server.ListenAndServe()
}
上述代码启动了一个 http 服务器,监听 8080 端口,分别实现了 /hello1
和 /hello2
两个路由。实现这两个路由的方法有点不同,一个使用 http.Handle
,另一个使用 http.HandleFunc
,下面来看看这两个之间的区别;
http.Handle
首先,简单分析一下 http.Handle(pattern string, handler Handler)
,http.Handle(pattern string, handler Handler)
接收两个参数,一个是路由匹配的字符串,另外一个是 Handler
类型的值:
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
然后由继续调用 DefaultServeMux.Handle(pattern string, handler Handler)
,该函数接收的参数与上面的函数一样:
func (mux *ServeMux) Handle(pattern string, handler Handler) {
...
}
这个 Handler
类型是什么呢,其实它就是一个接口,包含一个 ServeHttp()
的方法:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
所以,传入 http.Handle(pattern string, handler Handler)
第二个参数必须实现 ServeHTTP
这个方法,当接收到一个匹配路由的请求时,会调用该方法。
http.HandleFunc
该方法接收两个参数,一个是路由匹配的字符串,另外一个是 func(ResponseWriter, *Request)
类型的函数:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
然后继续调用 DefaultServeMux.HandleFunc(pattern, handler)
:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
可以看到,这里把 handler
转换成了 HandlerFunc
类型,而 HandlerFunc
类型则如下所示:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
该类型实现了 ServeHTTP
接口,所以其也可以转换成 Handler
类型,接下来调用 mux.Handle(pattern string, handler Handler)
就跟 http.Handle
的流程是一样的了。
#
golang http.handler 接口详解
1. 标准库接口定义
package http
type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}
func ListenAndServe(address string, h Handler) error
ListenAndServe函数需要一个例如“localhost:8000”的服务器地址,和一个处理所有请求的Handler接口实例。它会一直运行,直到这个服务因为一个错误而失败(或者启动失败),它的返回值一定是一个非空的错误。
2. 小 Demo
type dollars float32
func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }
type MyHandler map[string]dollars
func (self MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/list":
for item, price := range self {
fmt.Fprintf(w, "%s: %s\n", item, price)
}
case "/price":
item := req.URL.Query().Get("item")
price, ok := self[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)
}
}
func main() {
handler := MyHandler{"shoes": 50, "socks": 5}
log.Fatal(http.ListenAndServe("localhost:8000", handler))
}
现在handler基于URL的路径部分(req.URL.Path)来决定执行什么逻辑。如果这个handler不能识别这个路径,它会通过调用w.WriteHeader(http.StatusNotFound)返回客户端一个HTTP404错误
$ curl http://localhost:8000/list
shoes: $50.00
socks: $5.00
$ curl http://localhost:8000/price?item=socks
$5.00
$ curl http://localhost:8000/price?item=shoes
$50.00
$ curl http://localhost:8000/price?item=hat
no such item: "hat"
$ curl http://localhost:8000/help
no such page: /help
显然我们可以继续向ServeHTTP方法中添加case,但在一个实际的应用中,将每个case中的逻辑定义到一个分开的方法或函数中会很实用。对于更复杂的应用,一个ServeMux将一批http.Handler聚集到一个单一的http.Handler中,通过组合来处理更加错综复杂的路由需求。
3.ServeMux.Handle 改进版
type MyHandler map[string]dollars
func (self MyHandler) list(w http.ResponseWriter, req *http.Request) {
for item, price := range self {
fmt.Fprintf(w, "%s: %s\n", item, price)
}
}
func (self MyHandler) price(w http.ResponseWriter, req *http.Request) {
item := req.URL.Query().Get("item")
price, ok := self[item]
if !ok {
w.WriteHeader(http.StatusNotFound) // 404
fmt.Fprintf(w, "no such item: %q\n", item)
return
}
fmt.Fprintf(w, "%s\n", price)
}
func main() {
handler := MyHandler{"shoes": 50, "socks": 5}
mux := http.NewServeMux()
mux.Handle("/list", http.HandlerFunc(handler.list))
mux.Handle("/price", http.HandlerFunc(handler.price))
log.Fatal(http.ListenAndServe("localhost:8000", mux))
}
语句http.HandlerFunc(handler.list)是一个转换而非一个函数调用,因为http.HandlerFunc是一个类型。它有如下的定义:
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是一个让函数值满足一个接口的适配器(此处是http.Handler接口适配器,因为实现了ServeHTTP方法),这里函数和这个接口仅有的方法有相同的函数签名。实际上,这个技巧让一个单一的类型例如MyHandler以多种方式满足http.Handler接口:一种通过它的list方法,一种通过它的price方法等等。
4.ServeMux.HandleFunc 改进版
因为段落3中ServeMux.Handle方式注册非常普遍,ServeMux有一个方便的HandleFunc方法(ServeMux.Handle的该进),它帮我们简化handler注册代码成这样:
mux.HandleFunc("/list", handler.list)
mux.HandleFunc("/price", handler.price)
所以为了方便,net/http包提供了一个全局的ServeMux实例DefaultServerMux和包级别的http.Handle和http.HandleFunc函数。现在,为了使用DefaultServeMux作为服务器的主handler,我们不需要将它传给ListenAndServe函数;nil值就可以工作。
func main() {
handler := MyHandler{"shoes": 50, "socks": 5}
http.HandleFunc("/list", handler.list)
http.HandleFunc("/price", handler.price)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
代码与段落3区别在于不手动创建ServeMux实例,而是使用net/http包的DefaultServerMux全局实例.
More:
基于并发考虑,web服务器应该在一个新的协程中调用每一个handler,所以当handler获取其它协程或者这个handler本身的其它请求也可以访问的变量时一定要使用预防措施比如锁机制。