在Web开发中,正确处理HTTP响应是构建高质量Web应用的关键环节。Go语言的net/http包通过ResponseWriter接口为我们提供了强大而灵活的响应处理能力。掌握响应的状态码设置、头部管理和内容输出,不仅能提升用户体验,还能确保API的规范性和可维护性。
本文将深入探讨ResponseWriter接口的使用方法,从基础的状态码设置到高级的内容协商,再到构建完整的响应处理中间件系统。我们将通过丰富的实例和最佳实践,帮助你全面掌握Go Web响应处理的核心技术。
1. ResponseWriter 接口深度解析
1.1 接口结构与核心方法
ResponseWriter接口是Go HTTP服务器响应处理的核心,它定义了三个基本方法,每个方法都有其特定的用途和调用时机。
go
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
// ResponseAnalyzer 响应分析器,用于演示ResponseWriter的内部机制
type ResponseAnalyzer struct {
originalWriter http.ResponseWriter
statusCode int
headerWritten bool
bytesWritten int64
startTime time.Time
}
// NewResponseAnalyzer 创建响应分析器
func NewResponseAnalyzer(w http.ResponseWriter) *ResponseAnalyzer {
return &ResponseAnalyzer{
originalWriter: w,
statusCode: 200, // 默认状态码
startTime: time.Now(),
}
}
// Header 返回响应头部映射,用于设置响应头
func (ra *ResponseAnalyzer) Header() http.Header {
return ra.originalWriter.Header()
}
// Write 写入响应体数据
func (ra *ResponseAnalyzer) Write(data []byte) (int, error) {
// 如果还没有写入头部,则使用默认状态码200
if !ra.headerWritten {
ra.WriteHeader(ra.statusCode)
}
n, err := ra.originalWriter.Write(data)
ra.bytesWritten += int64(n)
return n, err
}
// WriteHeader 设置HTTP状态码并写入响应头
func (ra *ResponseAnalyzer) WriteHeader(statusCode int) {
if ra.headerWritten {
// 头部已经写入,不能再次设置
return
}
ra.statusCode = statusCode
ra.headerWritten = true
// 添加一些有用的响应头
ra.Header().Set("X-Response-Time", time.Since(ra.startTime).String())
ra.Header().Set("X-Server", "Go-HTTP-Server")
ra.originalWriter.WriteHeader(statusCode)
}
// GetStats 获取响应统计信息
func (ra *ResponseAnalyzer) GetStats() map[string]interface{} {
return map[string]interface{}{
"status_code": ra.statusCode,
"bytes_written": ra.bytesWritten,
"response_time": time.Since(ra.startTime).String(),
"header_written": ra.headerWritten,
}
}
// 演示ResponseWriter基础用法的处理器
func basicResponseDemo(w http.ResponseWriter, r *http.Request) {
analyzer := NewResponseAnalyzer(w)
// 1. 设置响应头部(必须在WriteHeader或Write之前)
analyzer.Header().Set("Content-Type", "application/json; charset=utf-8")
analyzer.Header().Set("Cache-Control", "no-cache")
analyzer.Header().Set("X-API-Version", "1.0")
// 2. 设置状态码(可选,默认为200)
analyzer.WriteHeader(http.StatusOK)
// 3. 写入响应体
response := map[string]interface{}{
"message": "ResponseWriter基础用法演示",
"method": r.Method,
"path": r.URL.Path,
"stats": analyzer.GetStats(),
}
json.NewEncoder(analyzer).Encode(response)
}
1.2 响应头部管理最佳实践
响应头部的正确设置对于Web应用的安全性、性能和兼容性至关重要。以下是一个完整的头部管理系统:
go
// HeaderManager 响应头部管理器
type HeaderManager struct {
defaultHeaders map[string]string
securityHeaders map[string]string
}
// NewHeaderManager 创建头部管理器
func NewHeaderManager() *HeaderManager {
return &HeaderManager{
defaultHeaders: map[string]string{
"Content-Type": "application/json; charset=utf-8",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
},
securityHeaders: map[string]string{
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"Content-Security-Policy": "default-src 'self'",
"Referrer-Policy": "strict-origin-when-cross-origin",
},
}
}
// SetDefaultHeaders 设置默认响应头
func (hm *HeaderManager) SetDefaultHeaders(w http.ResponseWriter) {
for key, value := range hm.defaultHeaders {
w.Header().Set(key, value)
}
}
// SetSecurityHeaders 设置安全相关头部
func (hm *HeaderManager) SetSecurityHeaders(w http.ResponseWriter) {
for key, value := range hm.securityHeaders {
w.Header().Set(key, value)
}
}
// SetCacheHeaders 设置缓存控制头部
func (hm *HeaderManager) SetCacheHeaders(w http.ResponseWriter, maxAge int, public bool) {
cacheControl := fmt.Sprintf("max-age=%d", maxAge)
if public {
cacheControl += ", public"
} else {
cacheControl += ", private"
}
w.Header().Set("Cache-Control", cacheControl)
w.Header().Set("Expires", time.Now().Add(time.Duration(maxAge)*time.Second).Format(http.TimeFormat))
}
// SetContentHeaders 设置内容相关头部
func (hm *HeaderManager) SetContentHeaders(w http.ResponseWriter, contentType string, contentLength int64) {
w.Header().Set("Content-Type", contentType)
if contentLength > 0 {
w.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
}
}
// SetCORSHeaders 设置跨域资源共享头部
func (hm *HeaderManager) SetCORSHeaders(w http.ResponseWriter, origin string, methods []string) {
if origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
if len(methods) > 0 {
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ", "))
}
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
w.Header().Set("Access-Control-Max-Age", "86400") // 24小时
}
// 演示头部管理的处理器
func headerManagementDemo(w http.ResponseWriter, r *http.Request) {
hm := NewHeaderManager()
// 设置基础头部
hm.SetDefaultHeaders(w)
// 根据请求类型设置不同的头部
switch r.URL.Query().Get("type") {
case "secure":
hm.SetSecurityHeaders(w)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "安全头部已设置", "headers": "security"}`)
case "cached":
hm.SetCacheHeaders(w, 3600, true) // 缓存1小时,公共缓存
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "缓存头部已设置", "cache_time": "1小时"}`)
case "cors":
hm.SetCORSHeaders(w, "*", []string{"GET", "POST", "PUT", "DELETE"})
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "CORS头部已设置", "origin": "*"}`)
default:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message": "默认头部已设置", "available_types": ["secure", "cached", "cors"]}`)
}
}
2. HTTP状态码处理与最佳实践
2.1 状态码分类与使用场景
HTTP状态码是客户端理解服务器响应的重要信息。正确使用状态码能够提升API的可用性和开发者体验。
go
// StatusCodeManager 状态码管理器
type StatusCodeManager struct {
customMessages map[int]string
}
// NewStatusCodeManager 创建状态码管理器
func NewStatusCodeManager() *StatusCodeManager {
return &StatusCodeManager{
customMessages: map[int]string{
// 2xx 成功状态码
200: "请求成功",
201: "资源创建成功",
202: "请求已接受,正在处理",
204: "请求成功,无返回内容",
// 3xx 重定向状态码
301: "资源已永久移动",
302: "资源临时重定向",
304: "资源未修改",
// 4xx 客户端错误状态码
400: "请求参数错误",
401: "未授权访问",
403: "禁止访问",
404: "资源不存在",
405: "请求方法不被允许",
409: "资源冲突",
422: "请求参数验证失败",
429: "请求过于频繁",
// 5xx 服务器错误状态码
500: "服务器内部错误",
502: "网关错误",
503: "服务不可用",
504: "网关超时",
},
}
}
// SendSuccess 发送成功响应
func (scm *StatusCodeManager) SendSuccess(w http.ResponseWriter, data interface{}, message string) {
scm.sendJSONResponse(w, http.StatusOK, map[string]interface{}{
"success": true,
"message": message,
"data": data,
})
}
// SendCreated 发送资源创建成功响应
func (scm *StatusCodeManager) SendCreated(w http.ResponseWriter, data interface{}, location string) {
if location != "" {
w.Header().Set("Location", location)
}
scm.sendJSONResponse(w, http.StatusCreated, map[string]interface{}{
"success": true,
"message": scm.customMessages[201],
"data": data,
})
}
// SendNoContent 发送无内容响应
func (scm *StatusCodeManager) SendNoContent(w http.ResponseWriter) {
w.WriteHeader(http.StatusNoContent)
}
// SendBadRequest 发送请求错误响应
func (scm *StatusCodeManager) SendBadRequest(w http.ResponseWriter, errors []string) {
scm.sendJSONResponse(w, http.StatusBadRequest, map[string]interface{}{
"success": false,
"message": scm.customMessages[400],
"errors": errors,
})
}
// SendUnauthorized 发送未授权响应
func (scm *StatusCodeManager) SendUnauthorized(w http.ResponseWriter, message string) {
w.Header().Set("WWW-Authenticate", "Bearer")
scm.sendJSONResponse(w, http.StatusUnauthorized, map[string]interface{}{
"success": false,
"message": message,
})
}
// SendForbidden 发送禁止访问响应
func (scm *StatusCodeManager) SendForbidden(w http.ResponseWriter, message string) {
scm.sendJSONResponse(w, http.StatusForbidden, map[string]interface{}{
"success": false,
"message": message,
})
}
// SendNotFound 发送资源不存在响应
func (scm *StatusCodeManager) SendNotFound(w http.ResponseWriter, resource string) {
scm.sendJSONResponse(w, http.StatusNotFound, map[string]interface{}{
"success": false,
"message": fmt.Sprintf("资源 '%s' 不存在", resource),
})
}
// SendMethodNotAllowed 发送方法不允许响应
func (scm *StatusCodeManager) SendMethodNotAllowed(w http.ResponseWriter, allowedMethods []string) {
w.Header().Set("Allow", strings.Join(allowedMethods, ", "))
scm.sendJSONResponse(w, http.StatusMethodNotAllowed, map[string]interface{}{
"success": false,
"message": scm.customMessages[405],
"allowed_methods": allowedMethods,
})
}
// SendConflict 发送资源冲突响应
func (scm *StatusCodeManager) SendConflict(w http.ResponseWriter, message string) {
scm.sendJSONResponse(w, http.StatusConflict, map[string]interface{}{
"success": false,
"message": message,
})
}
// SendValidationError 发送验证错误响应
func (scm *StatusCodeManager) SendValidationError(w http.ResponseWriter, validationErrors map[string][]string) {
scm.sendJSONResponse(w, http.StatusUnprocessableEntity, map[string]interface{}{
"success": false,
"message": scm.customMessages[422],
"errors": validationErrors,
})
}
// SendTooManyRequests 发送请求过于频繁响应
func (scm *StatusCodeManager) SendTooManyRequests(w http.ResponseWriter, retryAfter int) {
w.Header().Set("Retry-After", strconv.Itoa(retryAfter))
scm.sendJSONResponse(w, http.StatusTooManyRequests, map[string]interface{}{
"success": false,
"message": scm.customMessages[429],
"retry_after": retryAfter,
})
}
// SendInternalServerError 发送服务器内部错误响应
func (scm *StatusCodeManager) SendInternalServerError(w http.ResponseWriter, err error) {
scm.sendJSONResponse(w, http.StatusInternalServerError, map[string]interface{}{
"success": false,
"message": scm.customMessages[500],
"error": err.Error(),
})
}
// sendJSONResponse 发送JSON格式响应的通用方法
func (scm *StatusCodeManager) sendJSONResponse(w http.ResponseWriter, statusCode int, data interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(statusCode)
if err := json.NewEncoder(w).Encode(data); err != nil {
// 如果JSON编码失败,发送纯文本错误
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "JSON编码失败: %v", err)
}
}
// 演示状态码使用的处理器
func statusCodeDemo(w http.ResponseWriter, r *http.Request) {
scm := NewStatusCodeManager()
// 根据查询参数演示不同的状态码
action := r.URL.Query().Get("action")
switch action {
case "success":
scm.SendSuccess(w, map[string]string{"id": "123", "name": "测试用户"}, "用户信息获取成功")
case "created":
scm.SendCreated(w, map[string]string{"id": "456", "name": "新用户"}, "/users/456")
case "no_content":
scm.SendNoContent(w)
case "bad_request":
scm.SendBadRequest(w, []string{"用户名不能为空", "邮箱格式不正确"})
case "unauthorized":
scm.SendUnauthorized(w, "访问令牌已过期")
case "forbidden":
scm.SendForbidden(w, "您没有权限访问此资源")
case "not_found":
scm.SendNotFound(w, "用户")
case "method_not_allowed":
scm.SendMethodNotAllowed(w, []string{"GET", "POST"})
case "conflict":
scm.SendConflict(w, "用户名已存在")
case "validation_error":
validationErrors := map[string][]string{
"username": {"长度必须在3-20个字符之间", "只能包含字母和数字"},
"email": {"邮箱格式不正确"},
}
scm.SendValidationError(w, validationErrors)
case "too_many_requests":
scm.SendTooManyRequests(w, 60)
case "server_error":
scm.SendInternalServerError(w, fmt.Errorf("数据库连接失败"))
default:
scm.SendSuccess(w, map[string][]string{
"available_actions": {
"success", "created", "no_content", "bad_request",
"unauthorized", "forbidden", "not_found", "method_not_allowed",
"conflict", "validation_error", "too_many_requests", "server_error",
},
}, "状态码演示API")
}
}
2.2 内容协商与多格式响应
现代Web API需要支持多种响应格式,根据客户端的Accept头部返回相应的内容格式。
go
// ContentNegotiator 内容协商器
type ContentNegotiator struct {
supportedTypes map[string]func(w http.ResponseWriter, data interface{}) error
}
// NewContentNegotiator 创建内容协商器
func NewContentNegotiator() *ContentNegotiator {
cn := &ContentNegotiator{
supportedTypes: make(map[string]func(w http.ResponseWriter, data interface{}) error),
}
// 注册支持的内容类型
cn.supportedTypes["application/json"] = cn.writeJSON
cn.supportedTypes["application/xml"] = cn.writeXML
cn.supportedTypes["text/plain"] = cn.writeText
cn.supportedTypes["text/html"] = cn.writeHTML
return cn
}
// Negotiate 根据Accept头部协商内容类型
func (cn *ContentNegotiator) Negotiate(w http.ResponseWriter, r *http.Request, data interface{}) error {
acceptHeader := r.Header.Get("Accept")
// 如果没有Accept头部,默认使用JSON
if acceptHeader == "" {
acceptHeader = "application/json"
}
// 解析Accept头部,找到最匹配的内容类型
contentType := cn.parseAcceptHeader(acceptHeader)
// 查找对应的写入函数
writeFunc, exists := cn.supportedTypes[contentType]
if !exists {
// 不支持的内容类型,返回406错误
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotAcceptable)
return json.NewEncoder(w).Encode(map[string]interface{}{
"error": "不支持的内容类型",
"supported_types": cn.getSupportedTypes(),
"requested_type": acceptHeader,
})
}
return writeFunc(w, data)
}
// parseAcceptHeader 解析Accept头部,返回最佳匹配的内容类型
func (cn *ContentNegotiator) parseAcceptHeader(acceptHeader string) string {
// 简化的Accept头部解析,实际应用中可能需要更复杂的解析逻辑
acceptTypes := strings.Split(acceptHeader, ",")
for _, acceptType := range acceptTypes {
// 移除质量值和其他参数
contentType := strings.TrimSpace(strings.Split(acceptType, ";")[0])
// 检查是否支持此类型
if _, exists := cn.supportedTypes[contentType]; exists {
return contentType
}
// 处理通配符
if contentType == "*/*" || contentType == "application/*" {
return "application/json" // 默认返回JSON
}
}
return "application/json" // 默认返回JSON
}
// writeJSON 写入JSON格式响应
func (cn *ContentNegotiator) writeJSON(w http.ResponseWriter, data interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
return json.NewEncoder(w).Encode(data)
}
// writeXML 写入XML格式响应
func (cn *ContentNegotiator) writeXML(w http.ResponseWriter, data interface{}) error {
w.Header().Set("Content-Type", "application/xml; charset=utf-8")
w.WriteHeader(http.StatusOK)
// 简化的XML输出,实际应用中应使用encoding/xml包
fmt.Fprintf(w, `<?xml version="1.0" encoding="UTF-8"?>
<response>
<data>%+v</data>
</response>`, data)
return nil
}
// writeText 写入纯文本格式响应
func (cn *ContentNegotiator) writeText(w http.ResponseWriter, data interface{}) error {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "%+v", data)
return nil
}
// writeHTML 写入HTML格式响应
func (cn *ContentNegotiator) writeHTML(w http.ResponseWriter, data interface{}) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `<!DOCTYPE html>
<html>
<head>
<title>API响应</title>
<meta charset="utf-8">
</head>
<body>
<h1>API响应数据</h1>
<pre>%+v</pre>
</body>
</html>`, data)
return nil
}
// getSupportedTypes 获取支持的内容类型列表
func (cn *ContentNegotiator) getSupportedTypes() []string {
types := make([]string, 0, len(cn.supportedTypes))
for contentType := range cn.supportedTypes {
types = append(types, contentType)
}
return types
}
// 演示内容协商的处理器
func contentNegotiationDemo(w http.ResponseWriter, r *http.Request) {
cn := NewContentNegotiator()
// 准备响应数据
responseData := map[string]interface{}{
"message": "内容协商演示",
"timestamp": time.Now().Format(time.RFC3339),
"user_agent": r.Header.Get("User-Agent"),
"accept": r.Header.Get("Accept"),
"method": r.Method,
"path": r.URL.Path,
}
// 执行内容协商
if err := cn.Negotiate(w, r, responseData); err != nil {
http.Error(w, fmt.Sprintf("内容协商失败: %v", err), http.StatusInternalServerError)
}
}
3. 响应处理中间件系统
为了统一管理响应处理逻辑,我们可以构建一个完整的中间件系统,包括日志记录、性能监控和错误处理。
go
import (
"context"
"log"
"sync"
)
// ResponseMiddleware 响应处理中间件
type ResponseMiddleware struct {
headerManager *HeaderManager
statusCodeManager *StatusCodeManager
contentNegotiator *ContentNegotiator
logger *log.Logger
metrics *ResponseMetrics
}
// ResponseMetrics 响应指标统计
type ResponseMetrics struct {
mu sync.RWMutex
totalRequests int64
statusCounts map[int]int64
avgResponseTime time.Duration
}
// NewResponseMetrics 创建响应指标统计器
func NewResponseMetrics() *ResponseMetrics {
return &ResponseMetrics{
statusCounts: make(map[int]int64),
}
}
// RecordResponse 记录响应指标
func (rm *ResponseMetrics) RecordResponse(statusCode int, responseTime time.Duration) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.totalRequests++
rm.statusCounts[statusCode]++
// 计算平均响应时间(简化算法)
rm.avgResponseTime = (rm.avgResponseTime*time.Duration(rm.totalRequests-1) + responseTime) / time.Duration(rm.totalRequests)
}
// GetStats 获取统计信息
func (rm *ResponseMetrics) GetStats() map[string]interface{} {
rm.mu.RLock()
defer rm.mu.RUnlock()
return map[string]interface{}{
"total_requests": rm.totalRequests,
"status_counts": rm.statusCounts,
"avg_response_time": rm.avgResponseTime.String(),
}
}
// NewResponseMiddleware 创建响应中间件
func NewResponseMiddleware() *ResponseMiddleware {
return &ResponseMiddleware{
headerManager: NewHeaderManager(),
statusCodeManager: NewStatusCodeManager(),
contentNegotiator: NewContentNegotiator(),
logger: log.New(os.Stdout, "[RESPONSE] ", log.LstdFlags),
metrics: NewResponseMetrics(),
}
}
// Middleware 响应处理中间件函数
func (rm *ResponseMiddleware) Middleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
// 创建响应包装器
wrapper := &ResponseWrapper{
ResponseWriter: w,
statusCode: 200,
bytesWritten: 0,
}
// 设置默认头部
rm.headerManager.SetDefaultHeaders(wrapper)
// 在请求上下文中存储中间件实例
ctx := context.WithValue(r.Context(), "responseMiddleware", rm)
// 执行下一个处理器
next(wrapper, r.WithContext(ctx))
// 记录响应指标
responseTime := time.Since(startTime)
rm.metrics.RecordResponse(wrapper.statusCode, responseTime)
// 记录日志
rm.logger.Printf("%s %s - %d - %d bytes - %v",
r.Method, r.URL.Path, wrapper.statusCode, wrapper.bytesWritten, responseTime)
}
}
// ResponseWrapper 响应包装器,用于捕获响应信息
type ResponseWrapper struct {
http.ResponseWriter
statusCode int
bytesWritten int64
headerWritten bool
}
// WriteHeader 重写WriteHeader方法以捕获状态码
func (rw *ResponseWrapper) WriteHeader(statusCode int) {
if !rw.headerWritten {
rw.statusCode = statusCode
rw.headerWritten = true
rw.ResponseWriter.WriteHeader(statusCode)
}
}
// Write 重写Write方法以统计写入字节数
func (rw *ResponseWrapper) Write(data []byte) (int, error) {
if !rw.headerWritten {
rw.WriteHeader(200)
}
n, err := rw.ResponseWriter.Write(data)
rw.bytesWritten += int64(n)
return n, err
}
// GetResponseMiddleware 从上下文中获取响应中间件实例
func GetResponseMiddleware(r *http.Request) *ResponseMiddleware {
if rm, ok := r.Context().Value("responseMiddleware").(*ResponseMiddleware); ok {
return rm
}
return nil
}
// 便捷的响应方法,可以在处理器中直接使用
func SendJSONResponse(w http.ResponseWriter, r *http.Request, statusCode int, data interface{}) {
if rm := GetResponseMiddleware(r); rm != nil {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(data)
} else {
// 如果没有中间件,使用基本方法
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(data)
}
}
// SendErrorResponse 发送错误响应
func SendErrorResponse(w http.ResponseWriter, r *http.Request, statusCode int, message string, details interface{}) {
errorResponse := map[string]interface{}{
"success": false,
"message": message,
"timestamp": time.Now().Format(time.RFC3339),
}
if details != nil {
errorResponse["details"] = details
}
SendJSONResponse(w, r, statusCode, errorResponse)
}
// SendSuccessResponse 发送成功响应
func SendSuccessResponse(w http.ResponseWriter, r *http.Request, data interface{}, message string) {
successResponse := map[string]interface{}{
"success": true,
"message": message,
"data": data,
"timestamp": time.Now().Format(time.RFC3339),
}
SendJSONResponse(w, r, http.StatusOK, successResponse)
}
4. 综合实战:API响应管理系统
现在让我们构建一个完整的API响应管理系统,展示如何在实际项目中应用所有学到的响应处理技术。
go
// APIServer API服务器
type APIServer struct {
responseMiddleware *ResponseMiddleware
users map[int]*User
nextUserID int
mu sync.RWMutex
}
// User 用户结构
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}
// NewAPIServer 创建API服务器
func NewAPIServer() *APIServer {
return &APIServer{
responseMiddleware: NewResponseMiddleware(),
users: make(map[int]*User),
nextUserID: 1,
}
}
// GetUsers 获取用户列表
func (api *APIServer) GetUsers(w http.ResponseWriter, r *http.Request) {
api.mu.RLock()
users := make([]*User, 0, len(api.users))
for _, user := range api.users {
users = append(users, user)
}
api.mu.RUnlock()
// 支持分页
page := 1
limit := 10
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 {
limit = l
}
}
// 计算分页
start := (page - 1) * limit
end := start + limit
if start >= len(users) {
users = []*User{}
} else if end > len(users) {
users = users[start:]
} else {
users = users[start:end]
}
// 设置分页头部
w.Header().Set("X-Total-Count", strconv.Itoa(len(api.users)))
w.Header().Set("X-Page", strconv.Itoa(page))
w.Header().Set("X-Limit", strconv.Itoa(limit))
SendSuccessResponse(w, r, users, "用户列表获取成功")
}
// GetUser 获取单个用户
func (api *APIServer) GetUser(w http.ResponseWriter, r *http.Request) {
userID, err := api.extractUserID(r)
if err != nil {
SendErrorResponse(w, r, http.StatusBadRequest, "无效的用户ID", err.Error())
return
}
api.mu.RLock()
user, exists := api.users[userID]
api.mu.RUnlock()
if !exists {
SendErrorResponse(w, r, http.StatusNotFound, "用户不存在", nil)
return
}
SendSuccessResponse(w, r, user, "用户信息获取成功")
}
// CreateUser 创建用户
func (api *APIServer) CreateUser(w http.ResponseWriter, r *http.Request) {
var userData struct {
Username string `json:"username"`
Email string `json:"email"`
}
if err := json.NewDecoder(r.Body).Decode(&userData); err != nil {
SendErrorResponse(w, r, http.StatusBadRequest, "请求体解析失败", err.Error())
return
}
// 验证数据
if errors := api.validateUserData(userData.Username, userData.Email); len(errors) > 0 {
SendErrorResponse(w, r, http.StatusUnprocessableEntity, "数据验证失败", errors)
return
}
// 检查用户名是否已存在
api.mu.RLock()
for _, user := range api.users {
if user.Username == userData.Username {
api.mu.RUnlock()
SendErrorResponse(w, r, http.StatusConflict, "用户名已存在", nil)
return
}
}
api.mu.RUnlock()
// 创建用户
api.mu.Lock()
user := &User{
ID: api.nextUserID,
Username: userData.Username,
Email: userData.Email,
Created: time.Now(),
Updated: time.Now(),
}
api.users[api.nextUserID] = user
api.nextUserID++
api.mu.Unlock()
// 设置Location头部
w.Header().Set("Location", fmt.Sprintf("/users/%d", user.ID))
SendJSONResponse(w, r, http.StatusCreated, map[string]interface{}{
"success": true,
"message": "用户创建成功",
"data": user,
})
}
// UpdateUser 更新用户
func (api *APIServer) UpdateUser(w http.ResponseWriter, r *http.Request) {
userID, err := api.extractUserID(r)
if err != nil {
SendErrorResponse(w, r, http.StatusBadRequest, "无效的用户ID", err.Error())
return
}
var updateData struct {
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&updateData); err != nil {
SendErrorResponse(w, r, http.StatusBadRequest, "请求体解析失败", err.Error())
return
}
api.mu.Lock()
defer api.mu.Unlock()
user, exists := api.users[userID]
if !exists {
SendErrorResponse(w, r, http.StatusNotFound, "用户不存在", nil)
return
}
// 更新字段
if updateData.Username != "" {
user.Username = updateData.Username
}
if updateData.Email != "" {
user.Email = updateData.Email
}
user.Updated = time.Now()
SendSuccessResponse(w, r, user, "用户更新成功")
}
// DeleteUser 删除用户
func (api *APIServer) DeleteUser(w http.ResponseWriter, r *http.Request) {
userID, err := api.extractUserID(r)
if err != nil {
SendErrorResponse(w, r, http.StatusBadRequest, "无效的用户ID", err.Error())
return
}
api.mu.Lock()
defer api.mu.Unlock()
if _, exists := api.users[userID]; !exists {
SendErrorResponse(w, r, http.StatusNotFound, "用户不存在", nil)
return
}
delete(api.users, userID)
w.WriteHeader(http.StatusNoContent)
}
// GetMetrics 获取API指标
func (api *APIServer) GetMetrics(w http.ResponseWriter, r *http.Request) {
metrics := api.responseMiddleware.metrics.GetStats()
SendSuccessResponse(w, r, metrics, "指标获取成功")
}
// extractUserID 从URL路径中提取用户ID
func (api *APIServer) extractUserID(r *http.Request) (int, error) {
pathParts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(pathParts) < 2 {
return 0, fmt.Errorf("缺少用户ID")
}
return strconv.Atoi(pathParts[1])
}
// validateUserData 验证用户数据
func (api *APIServer) validateUserData(username, email string) map[string][]string {
errors := make(map[string][]string)
if username == "" {
errors["username"] = append(errors["username"], "用户名不能为空")
} else if len(username) < 3 || len(username) > 20 {
errors["username"] = append(errors["username"], "用户名长度必须在3-20个字符之间")
}
if email == "" {
errors["email"] = append(errors["email"], "邮箱不能为空")
} else if !strings.Contains(email, "@") {
errors["email"] = append(errors["email"], "邮箱格式不正确")
}
return errors
}
// setupRoutes 设置路由
func (api *APIServer) setupRoutes() *http.ServeMux {
mux := http.NewServeMux()
// 用户相关路由
mux.HandleFunc("/users", api.responseMiddleware.Middleware(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
api.GetUsers(w, r)
case http.MethodPost:
api.CreateUser(w, r)
default:
rm := GetResponseMiddleware(r)
if rm != nil {
rm.statusCodeManager.SendMethodNotAllowed(w, []string{"GET", "POST"})
}
}
}))
mux.HandleFunc("/users/", api.responseMiddleware.Middleware(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
api.GetUser(w, r)
case http.MethodPut:
api.UpdateUser(w, r)
case http.MethodDelete:
api.DeleteUser(w, r)
default:
rm := GetResponseMiddleware(r)
if rm != nil {
rm.statusCodeManager.SendMethodNotAllowed(w, []string{"GET", "PUT", "DELETE"})
}
}
}))
// 指标路由
mux.HandleFunc("/metrics", api.responseMiddleware.Middleware(api.GetMetrics))
// 演示路由
mux.HandleFunc("/demo/basic", api.responseMiddleware.Middleware(basicResponseDemo))
mux.HandleFunc("/demo/headers", api.responseMiddleware.Middleware(headerManagementDemo))
mux.HandleFunc("/demo/status", api.responseMiddleware.Middleware(statusCodeDemo))
mux.HandleFunc("/demo/content", api.responseMiddleware.Middleware(contentNegotiationDemo))
return mux
}
// 主函数
func main() {
api := NewAPIServer()
mux := api.setupRoutes()
fmt.Println("API服务器启动在 http://localhost:8080")
fmt.Println("可用的端点:")
fmt.Println(" GET /users - 获取用户列表")
fmt.Println(" POST /users - 创建用户")
fmt.Println(" GET /users/{id} - 获取单个用户")
fmt.Println(" PUT /users/{id} - 更新用户")
fmt.Println(" DELETE /users/{id} - 删除用户")
fmt.Println(" GET /metrics - 获取API指标")
fmt.Println(" GET /demo/* - 各种演示端点")
if err := http.ListenAndServe(":8080", mux); err != nil {
fmt.Printf("服务器启动失败: %v\n", err)
}
}
5. 总结
本文全面探讨了Go Web编程中的响应处理技术,涵盖了从基础的ResponseWriter接口使用到高级的响应处理中间件系统:
-
ResponseWriter接口深度解析:详细介绍了接口的三个核心方法及其使用时机,展示了如何正确设置响应头部和状态码
-
HTTP状态码处理与最佳实践:构建了完整的状态码管理系统,涵盖了各种常见场景的状态码使用规范
-
响应头部管理:实现了灵活的头部管理器,支持安全头部、缓存控制、CORS等多种场景
-
内容协商机制:展示了如何根据客户端需求返回不同格式的响应内容,提升API的灵活性
-
响应处理中间件系统:构建了统一的响应处理框架,集成了日志记录、性能监控和错误处理
通过API响应管理系统的综合实战,我们展示了如何将这些技术有机结合,构建出功能完整、易于维护的Web应用。掌握这些响应处理技术,将为你开发高质量的Go Web应用奠定坚实的基础。
在下一篇文章中,我们将探讨中间件的设计与实现,学习如何构建可复用的请求处理管道,进一步提升Web应用的架构质量。