Go Web 编程快速入门 06 - 响应 ResponseWriter:状态码与头部

在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接口使用到高级的响应处理中间件系统:

  1. ResponseWriter接口深度解析:详细介绍了接口的三个核心方法及其使用时机,展示了如何正确设置响应头部和状态码

  2. HTTP状态码处理与最佳实践:构建了完整的状态码管理系统,涵盖了各种常见场景的状态码使用规范

  3. 响应头部管理:实现了灵活的头部管理器,支持安全头部、缓存控制、CORS等多种场景

  4. 内容协商机制:展示了如何根据客户端需求返回不同格式的响应内容,提升API的灵活性

  5. 响应处理中间件系统:构建了统一的响应处理框架,集成了日志记录、性能监控和错误处理

通过API响应管理系统的综合实战,我们展示了如何将这些技术有机结合,构建出功能完整、易于维护的Web应用。掌握这些响应处理技术,将为你开发高质量的Go Web应用奠定坚实的基础。

在下一篇文章中,我们将探讨中间件的设计与实现,学习如何构建可复用的请求处理管道,进一步提升Web应用的架构质量。

相关推荐
chao1898444 小时前
C#模拟鼠标键盘操作的多种实现方案
开发语言·c#·计算机外设
mit6.8244 小时前
[Agent可视化] 编排工作流(Go) | Temporal引擎 | DAG调度器 | ReAct模式实现
开发语言·后端·golang
Devil枫4 小时前
HarmonyOS鸿蒙应用:仓颉语言与JavaScript核心差异深度解析
开发语言·javascript·ecmascript
惺忪97984 小时前
回调函数的概念
开发语言·前端·javascript
pcm1235674 小时前
java中的单例模式
java·开发语言·单例模式
kaikaile19954 小时前
Java面试题总结
开发语言·python
wuk9985 小时前
C#和NModbus库实现Modbus从站
开发语言·c#
周周记笔记5 小时前
Python及Ipython解释器
开发语言·python
oioihoii5 小时前
当无符号与有符号整数相遇:C++中的隐式类型转换陷阱
java·开发语言·c++