在构建 Go API 时,日志记录和错误处理是至关重要的组成部分,它们可以帮助你调试代码、监控 API 行为并为用户提供友好的错误提示。中间件提供了一种优雅的方式来实现这些功能,它允许你在请求到达你的 API 端点之前拦截请求并执行一些操作。
本文将引导你逐步添加日志记录和错误处理中间件到你的 Go API 中,使你的 API 更加健壮、易于维护和用户友好。
什么是中间件?
中间件就像你最喜欢的夜总会里的保安,它会在请求到达你的 API 端点之前拦截请求。你可以使用中间件来检查身份验证(比如我们在 JWT 中所做的那样)、记录信息或在出现错误时进行处理。
今天,我们将构建一个中间件,它可以:
- 记录:每个传入请求,以便我们知道谁在敲打我们的 API 的门。
- 处理错误:优雅地处理错误,这样你的用户就不会看到那些难看的 500 错误。
让我们深入研究吧!
第一步:创建日志记录中间件
日志记录是你调试和了解 API 中发生的事情时最好的朋友。我们将创建一个中间件,它记录每个传入请求——方法、URL 和所用时间。
import (
"log"
"net/http"
"time"
)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 记录方法和请求的 URL
log.Printf("Started %s %s", r.Method, r.URL.Path)
// 调用链中的下一个处理程序
next.ServeHTTP(w, r)
// 记录所用时间
log.Printf("Completed in %v", time.Since(start))
})
}
这段代码定义了一个名为 loggingMiddleware 的函数,它接受一个 http.Handler 作为参数,并返回一个新的 http.Handler。这个新处理程序包含一个匿名函数,它会在每个请求到达时执行。在函数中,我们首先记录了请求的开始时间,然后记录了请求的方法和 URL。接下来,我们调用 next.ServeHTTP 将请求传递给链中的下一个处理程序。最后,我们记录了请求的处理时间。
第二步:错误处理中间件
让我们谈谈错误。错误会发生,对吧?但是,与其让它们导致崩溃或发送模糊的错误消息,不如让我们优雅地处理它们。
func errorHandlingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
// 记录错误并发送用户友好的消息
log.Printf("Error occurred: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
这段代码定义了一个名为 errorHandlingMiddleware 的函数,它接受一个 http.Handler 作为参数,并返回一个新的 http.Handler。这个新处理程序包含一个匿名函数,它会在每个请求到达时执行。在函数中,我们使用 defer 关键字来确保在函数返回之前执行一个匿名函数。这个匿名函数使用 recover() 函数来捕获任何恐慌,并记录错误信息,然后向客户端发送一个 500 错误。
第三步:在你的 API 中集成中间件
现在我们已经构建了日志记录和错误处理中间件,让我们将它们连接到我们的 API。我们将全局应用它们,这样每个请求都会被记录,并且错误会被捕获。
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
// ... 其他代码 ...
func main() {
// ... 其他代码 ...
r := mux.NewRouter()
// 全局应用中间件
r.Use(loggingMiddleware)
r.Use(errorHandlingMiddleware)
// ... 其他代码 ...
fmt.Println("Server started on port :8000")
log.Fatal(http.ListenAndServe(":8000", r))
}
在 main 函数中,我们首先创建了一个 mux.NewRouter 对象,它用于路由请求。然后,我们使用 r.Use 函数全局应用了 loggingMiddleware 和 errorHandlingMiddleware。这样,所有请求都会经过这两个中间件。
第四步:测试它
为了确保一切正常,启动你的 API:
go run main.go
现在,尝试访问你的任何端点(例如 /books)并检查你的终端。你应该看到类似以下的日志:
Started GET /books
Completed in 1.2ms
如果出现错误,你会看到:
Error occurred: some error details
但是,你的用户只会看到一条干净的“500 内部服务器错误”消息。🎉
为什么这很重要?
- 日志记录有助于你跟踪错误和监控 API 的行为。如果出现问题,你会确切地知道哪个端点被访问以及请求花费了多长时间。
- 错误处理可以防止你的 API 在出现意外情况时崩溃。相反,它会优雅地恢复并向客户端发送一条清晰的错误消息。