如何让Go中间件验证失败时终止请求,不进入后续处理
问题描述
我有一个自建服务器,目前只有一个端点,所有请求都应该来自指定的单一来源——这个来源会在发数据包前做签名。我想在服务器端对所有请求做签名验证,但不想把验证逻辑写在处理器(handler)里,而是放在中间件层,这样代码更整洁,以后新增授权客户端可调用的端点时,也不用在新的处理器/路由里重复写验证逻辑。
现在遇到的问题是:不知道怎么在中间件里触发错误并终止请求,让验证失败的时候,请求不会走到后续的处理步骤里。
当前代码如下:main.go中的路由配置:
server.Router.Use(verifySignatureMiddleware) // server.Router 是 *chi.Mux 类型的路由实例 server.Router.Post("/", dummyHandler.ServeHTTP)
签名验证中间件的定义:
func verifySignatureMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { bodyBytes, err := io.ReadAll(r.Body) if err != nil { // 打印错误并终止请求处理 } isValid, err = otherLogicForSignatureValidation() if err != nil { // 打印错误并终止请求处理 } if isValid == false { // 打印错误并终止请求处理 } next.ServeHTTP(w, r) }) }
中间件可能在多个环节失败,我希望任何失败情况都能终止HTTP请求处理,同时记录错误。
解决方案
要在中间件里终止请求,核心就是不调用next.ServeHTTP(w, r),同时给客户端返回合适的HTTP错误响应,再把错误日志记下来就行。另外要注意,读取请求体之后,得把r.Body恢复成可读取的状态,不然验证成功后,后续的handler就拿不到请求内容了。
修改后的中间件代码示例:
import ( "io" "log" "net/http" "bytes" ) func verifySignatureMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 读取请求体内容 bodyBytes, err := io.ReadAll(r.Body) if err != nil { log.Printf("读取请求体失败: %v", err) http.Error(w, "请求处理失败", http.StatusBadRequest) return // 直接返回,跳过后续handler的执行 } // 恢复请求体,让后续handler能正常读取 r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 执行签名验证逻辑(这里假设需要传入请求体) isValid, err := otherLogicForSignatureValidation(bodyBytes) if err != nil { log.Printf("签名验证逻辑出错: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } if !isValid { log.Println("签名验证失败,请求来源非法") http.Error(w, "未授权的请求", http.StatusUnauthorized) return } // 所有验证通过,继续执行后续的handler next.ServeHTTP(w, r) }) }
关键要点
- 每个错误分支都用
return结束当前函数,这样就不会调用next.ServeHTTP,请求流程直接终止 - 用
http.Error()可以快速返回带状态码的标准HTTP错误响应,给客户端明确的提示 - 读取请求体后必须用
io.NopCloser和bytes.NewBuffer重新包装,恢复请求体的可读取性,否则后续handler无法获取请求内容 - 用
log.Printf记录错误详情,方便后续排查问题
你可以根据业务场景调整返回的HTTP状态码:
- 签名验证失败(非法来源):用
http.StatusUnauthorized(401)或者http.StatusForbidden(403) - 读取请求体失败:用
http.StatusBadRequest(400) - 验证逻辑内部出错:用
http.StatusInternalServerError(500)
内容的提问来源于stack exchange,提问作者Tarta
相关产品推荐
相关产品推荐

