答案:通过统一响应格式、中间件捕获panic、主动处理业务错误和自定义错误类型,构建go Web服务的健壮异常处理体系,避免裸露panic,确保错误可控并返回结构化信息。

在Go语言开发Web服务时,接口异常处理是保障系统稳定性和可维护性的关键环节。很多初学者直接在Handler中使用panic或忽略错误,导致服务崩溃或返回不一致的响应格式。一个良好的异常处理机制应统一捕获错误、记录日志,并返回结构化的错误信息给前端。
统一错误响应格式
定义一致的API响应结构,便于前端解析和展示错误信息:
<code>type Response struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } func JSON(w http.ResponseWriter, status int, data interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(data) } func Error(w http.ResponseWriter, code int, message string) { JSON(w, code, Response{ Code: code, Message: message, }) }
中间件捕获Panic异常
使用中间件在请求处理链中捕获意外panic,防止服务中断:
<code>func Recoverer(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("panic recovered: %vn", err) log.Printf("stack trace: %s", debug.Stack()) Error(w, 500, "Internal Server Error") } }() next.ServeHTTP(w, r) }) }
立即学习“go语言免费学习笔记(深入)”;
主动处理业务错误
避免在Handler中直接panic,而是通过error判断进行处理:
<code>func GetUserHandler(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("id") if userID == "" { Error(w, 400, "Missing user id") return } user, err := db.GetUser(userID) if err != nil { if errors.Is(err, sql.ErrNoRows) { Error(w, 404, "User not found") return } log.Printf("db error: %v", err) Error(w, 500, "Failed to fetch user") return } JSON(w, 200, Response{ Code: 0, Message: "success", Data: user, }) }
根据错误类型返回不同状态码和提示,同时记录必要日志。
自定义错误类型增强控制
定义应用级错误类型,便于分类处理:
<code>type AppError struct { Code int Message string } func (e AppError) Error() string { return e.Message }
在业务逻辑中返回AppError,在中间件中统一拦截并转换为HTTP响应:
<code>if err != nil { if appErr, ok := err.(AppError); ok { Error(w, appErr.Code, appErr.Message) return } // 其他错误按500处理 Error(w, 500, "Internal error") return }
基本上就这些。通过统一响应格式、中间件恢复panic、主动错误判断和自定义错误类型,可以构建出健壮的golang Web异常处理体系。关键是避免裸露的panic,始终控制错误输出边界。