Skip to content
🎉 httpz v1.1.0 已发布
Docs手写httpz错误集中处理

net/http 处理错误的一般流程如下:

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /hello", helloHandler)
	http.ListenAndServe(":8080", mux)
}
 
func helloHandler(w http.ResponseWriter, r *http.Request) {
	condition := false
	if !condition {
		http.Error(w, "bad reqeust", http.StatusBadRequest)
		return
	}
	
	fmt.Fprintf(w, "hello world")
}

设想一下,你在使用net/http 时,想添加错误集中处理的功能。

那么helloHandler就必须返回一个error

func helloHandlerWithErr(w http.ResponseWriter, r *http.Request) error {
	condition := false
	if !condition {
		return fmt.Errorf("condition is false")
	}
 
	fmt.Fprintf(w, "hello world")
	return nil
}

可是http.HandleFunc只接受func(http.ResponseWriter, *http.Request)签名的handler函数:

HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))

你想到了写一个适配器,把

type HandlerFunc func(http.ResponseWriter, *http.Request) error

转化成

type HttpHandlerFunc func(http.ResponseWriter, *http.Request)

动手实现,你在适配器中进行集中错误处理:

func Adator(fn HandlerFunc) HttpHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		err := fn(w, r)
 
		// 集中错误处理
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
	}
}

你已经实现了集中错误处理,但是需要你使用Adator对每个HanderFunc进行包裹:

mux.HandleFunc("GET /hello", Adator(helloHandlerWithErr))

这是很繁琐的,我们需要进一步优化。

创建一个结构体ServeMux它嵌入了http.ServeMux:

type ServeMux struct {
	http.ServeMux
}

重写ServeMuxHandleFunc方法, 它接收HandlerFunc而不是原来的HttpHandlerFunc

func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) {
	m.ServeMux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
		err := handler(w, r)
 
		if err != nil {
			http.Error(w, "some msg", http.StatusBadRequest)
		}
	})
}

这样就不需要使用Adator进行包裹了。完整的实现在 https://github.com/aeilang/httpz/blob/main/http.go