golang-http-frameworks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo HTTP Frameworks & REST APIs
Go HTTP框架与REST API
Overview
概述
Go provides exceptional HTTP capabilities starting with the standard library's package. Go 1.22+ introduced enhanced pattern routing in , making stdlib viable for many applications. For more complex needs, frameworks like Chi, Gin, Echo, and Fiber offer additional features while maintaining Go's simplicity and performance.
net/httpServeMuxKey Features:
- 🌐 net/http: Production-ready standard library with Go 1.22+ routing
- 🎯 Chi: Lightweight, stdlib-compatible router with middleware chains
- ⚡ Gin: High-performance framework with binding and validation
- 🛡️ Echo: Type-safe, enterprise framework with OpenAPI support
- 🚀 Fiber: Express.js-inspired framework with WebSocket support
- 🔧 Middleware: Composable request/response processing
- ✅ Validation: Struct tag-based request validation
- 🧪 Testing: httptest.Server for comprehensive integration tests
Go 从标准库的包开始就提供了出色的HTTP处理能力。Go 1.22+版本在中引入了增强的模式路由,使得标准库足以满足许多应用的需求。对于更复杂的场景,Chi、Gin、Echo和Fiber等框架在保持Go简洁性和高性能的同时,提供了更多附加功能。
net/httpServeMux核心特性:
- 🌐 net/http:Go 1.22+版本路由支持的生产级标准库
- 🎯 Chi:轻量级、兼容标准库的路由器,支持中间件链
- ⚡ Gin:具备绑定和验证功能的高性能框架
- 🛡️ Echo:支持OpenAPI的类型安全企业级框架
- 🚀 Fiber:受Express.js启发、支持WebSocket的框架
- 🔧 中间件:可组合的请求/响应处理机制
- ✅ 验证:基于结构体标签的请求验证
- 🧪 测试:使用httptest.Server进行全面集成测试
When to Use This Skill
何时使用该技能
Activate this skill when:
- Building RESTful APIs or web services
- Choosing appropriate HTTP framework for project requirements
- Implementing authentication or authorization middleware
- Designing REST endpoint patterns and validation
- Testing HTTP handlers and middleware chains
- Optimizing API performance and response times
- Migrating between HTTP frameworks
在以下场景中启用此技能:
- 构建RESTful API或Web服务
- 根据项目需求选择合适的HTTP框架
- 实现认证或授权中间件
- 设计REST端点模式与验证规则
- 测试HTTP处理器和中间件链
- 优化API性能和响应时间
- 在不同HTTP框架之间进行迁移
Framework Selection Guide
框架选择指南
net/http (Standard Library) - Go 1.22+
net/http(标准库)- Go 1.22+
Use When:
- Building simple to moderate complexity APIs
- Avoiding external dependencies is priority
- Need maximum compatibility and long-term stability
- Team prefers explicit over implicit patterns
Strengths:
- Zero dependencies, part of Go standard library
- Go 1.22+ pattern routing with path parameters
- Excellent performance and stability
- Extensive ecosystem compatibility
- No framework lock-in
Limitations:
- More verbose middleware composition
- Manual request validation
- No built-in binding or rendering
Example:
go
package main
import (
"encoding/json"
"net/http"
"log"
)
// Go 1.22+ pattern routing
func main() {
mux := http.NewServeMux()
// Path parameters with {param} syntax
mux.HandleFunc("GET /users/{id}", getUserHandler)
mux.HandleFunc("POST /users", createUserHandler)
mux.HandleFunc("GET /users", listUsersHandler)
// Middleware wrapping
handler := loggingMiddleware(mux)
log.Fatal(http.ListenAndServe(":8080", handler))
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // Go 1.22+ path parameter extraction
user := User{ID: id, Name: "John Doe"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}适用场景:
- 构建简单到中等复杂度的API
- 优先考虑避免外部依赖
- 需要最高兼容性和长期稳定性
- 团队偏好显式而非隐式模式
优势:
- 零依赖,属于Go标准库的一部分
- Go 1.22+版本支持带路径参数的模式路由
- 卓越的性能和稳定性
- 广泛的生态系统兼容性
- 无框架锁定
局限性:
- 中间件组合更繁琐
- 需要手动进行请求验证
- 无内置绑定或渲染功能
示例:
go
package main
import (
"encoding/json"
"net/http"
"log"
)
// Go 1.22+ pattern routing
func main() {
mux := http.NewServeMux()
// Path parameters with {param} syntax
mux.HandleFunc("GET /users/{id}", getUserHandler)
mux.HandleFunc("POST /users", createUserHandler)
mux.HandleFunc("GET /users", listUsersHandler)
// Middleware wrapping
handler := loggingMiddleware(mux)
log.Fatal(http.ListenAndServe(":8080", handler))
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // Go 1.22+ path parameter extraction
user := User{ID: id, Name: "John Doe"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}Chi - Lightweight Router
Chi - 轻量级路由器
Use When:
- Want stdlib-compatible router with better ergonomics
- Need clean middleware composition
- Prefer explicit over magic patterns
- Building moderate to complex routing structures
Strengths:
- 100% compatible with
net/http - Excellent middleware ecosystem
- Route grouping and nesting
- Context-based parameter passing
- Minimal performance overhead
Installation:
bash
go get -u github.com/go-chi/chi/v5Example:
go
package main
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
// Built-in middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
// Route grouping
r.Route("/api/v1", func(r chi.Router) {
r.Route("/users", func(r chi.Router) {
r.Get("/", listUsers)
r.Post("/", createUser)
r.Route("/{userID}", func(r chi.Router) {
r.Use(UserContext) // Middleware for nested routes
r.Get("/", getUser)
r.Put("/", updateUser)
r.Delete("/", deleteUser)
})
})
})
http.ListenAndServe(":8080", r)
}
func UserContext(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
// Load user from database, set in context
ctx := context.WithValue(r.Context(), "user", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getUser(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("user").(string)
// Return user data
json.NewEncoder(w).Encode(map[string]string{"id": userID})
}适用场景:
- 想要兼容标准库且更易用的路由器
- 需要清晰的中间件组合方式
- 偏好显式而非魔法模式
- 构建中等至复杂的路由结构
优势:
- 100%兼容
net/http - 优秀的中间件生态系统
- 支持路由分组和嵌套
- 基于上下文的参数传递
- 极低的性能开销
安装:
bash
go get -u github.com/go-chi/chi/v5示例:
go
package main
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
// Built-in middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
// Route grouping
r.Route("/api/v1", func(r chi.Router) {
r.Route("/users", func(r chi.Router) {
r.Get("/", listUsers)
r.Post("/", createUser)
r.Route("/{userID}", func(r chi.Router) {
r.Use(UserContext) // Middleware for nested routes
r.Get("/", getUser)
r.Put("/", updateUser)
r.Delete("/", deleteUser)
})
})
})
http.ListenAndServe(":8080", r)
}
func UserContext(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
// Load user from database, set in context
ctx := context.WithValue(r.Context(), "user", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getUser(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("user").(string)
// Return user data
json.NewEncoder(w).Encode(map[string]string{"id": userID})
}Gin - High Performance Framework
Gin - 高性能框架
Use When:
- Need maximum performance (8x faster than most frameworks)
- Want batteries-included experience
- Require built-in validation and binding
- Building JSON APIs with minimal boilerplate
Strengths:
- Extremely fast (fastest Go framework in benchmarks)
- Built-in JSON binding and validation
- Middleware ecosystem
- Group-based routing
- Custom error handling
Installation:
bash
go get -u github.com/gin-gonic/ginExample:
go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type CreateUserRequest struct {
Name string `json:"name" binding:"required,min=3"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"required,gte=18"`
}
func main() {
r := gin.Default() // Logger + Recovery middleware
api := r.Group("/api/v1")
{
users := api.Group("/users")
{
users.GET("", listUsers)
users.POST("", createUser)
users.GET("/:id", getUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
}
}
r.Run(":8080")
}
func createUser(c *gin.Context) {
var req CreateUserRequest
// Automatic validation based on struct tags
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Process user creation
user := User{
ID: generateID(),
Name: req.Name,
Email: req.Email,
Age: req.Age,
}
c.JSON(http.StatusCreated, user)
}
func getUser(c *gin.Context) {
id := c.Param("id")
// Return user
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": "John Doe",
})
}适用场景:
- 需要极致性能(比大多数框架快8倍)
- 想要开箱即用的体验
- 需要内置验证和绑定功能
- 以最少样板代码构建JSON API
优势:
- 极快的速度(基准测试中最快的Go框架)
- 内置JSON绑定和验证
- 丰富的中间件生态系统
- 基于分组的路由
- 自定义错误处理
安装:
bash
go get -u github.com/gin-gonic/gin示例:
go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type CreateUserRequest struct {
Name string `json:"name" binding:"required,min=3"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"required,gte=18"`
}
func main() {
r := gin.Default() // Logger + Recovery middleware
api := r.Group("/api/v1")
{
users := api.Group("/users")
{
users.GET("", listUsers)
users.POST("", createUser)
users.GET("/:id", getUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
}
}
r.Run(":8080")
}
func createUser(c *gin.Context) {
var req CreateUserRequest
// Automatic validation based on struct tags
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Process user creation
user := User{
ID: generateID(),
Name: req.Name,
Email: req.Email,
Age: req.Age,
}
c.JSON(http.StatusCreated, user)
}
func getUser(c *gin.Context) {
id := c.Param("id")
// Return user
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": "John Doe",
})
}Echo - Enterprise Framework
Echo - 企业级框架
Use When:
- Building enterprise applications
- Need OpenAPI/Swagger integration
- Want comprehensive middleware library
- Require type-safe routing and binding
Strengths:
- Type-safe routing with automatic parameter binding
- Built-in middleware for common patterns
- OpenAPI/Swagger generation support
- Excellent error handling middleware
- WebSocket support
Installation:
bash
go get -u github.com/labstack/echo/v4Example:
go
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
// Routes
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)
e.Logger.Fatal(e.Start(":8080"))
}
func getUser(c echo.Context) error {
id := c.Param("id")
user := User{ID: id, Name: "John Doe"}
return c.JSON(http.StatusOK, user)
}
func createUser(c echo.Context) error {
var user User
if err := c.Bind(&user); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if err := c.Validate(&user); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusCreated, user)
}
// Custom error handler
func customErrorHandler(err error, c echo.Context) {
code := http.StatusInternalServerError
message := "Internal Server Error"
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
message = he.Message.(string)
}
c.JSON(code, map[string]string{"error": message})
}适用场景:
- 构建企业级应用
- 需要OpenAPI/Swagger集成
- 想要全面的中间件库
- 需要类型安全的路由和绑定
优势:
- 类型安全的路由,支持自动参数绑定
- 内置常见模式的中间件
- 支持OpenAPI/Swagger生成
- 优秀的错误处理中间件
- 支持WebSocket
安装:
bash
go get -u github.com/labstack/echo/v4示例:
go
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
// Routes
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)
e.Logger.Fatal(e.Start(":8080"))
}
func getUser(c echo.Context) error {
id := c.Param("id")
user := User{ID: id, Name: "John Doe"}
return c.JSON(http.StatusOK, user)
}
func createUser(c echo.Context) error {
var user User
if err := c.Bind(&user); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if err := c.Validate(&user); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusCreated, user)
}
// Custom error handler
func customErrorHandler(err error, c echo.Context) {
code := http.StatusInternalServerError
message := "Internal Server Error"
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
message = he.Message.(string)
}
c.JSON(code, map[string]string{"error": message})
}Fiber - Express.js Style
Fiber - Express.js风格
Use When:
- Team familiar with Express.js patterns
- Need WebSocket support out of the box
- Building real-time applications
- Want fastest route matching performance
Strengths:
- Express.js-inspired API (easy for Node.js developers)
- Fastest route matching (uses fasthttp)
- Built-in WebSocket support
- Template engine support
- File upload handling
Limitations:
- Uses fasthttp instead of net/http (less ecosystem compatibility)
- Not compatible with standard http.Handler interface
- Slightly less mature ecosystem
Installation:
bash
go get -u github.com/gofiber/fiber/v2Example:
go
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/cors"
)
func main() {
app := fiber.New()
// Middleware
app.Use(logger.New())
app.Use(cors.New())
// Routes
api := app.Group("/api/v1")
users := api.Group("/users")
users.Get("/", listUsers)
users.Post("/", createUser)
users.Get("/:id", getUser)
users.Put("/:id", updateUser)
users.Delete("/:id", deleteUser)
app.Listen(":8080")
}
func getUser(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{
"id": id,
"name": "John Doe",
})
}
func createUser(c *fiber.Ctx) error {
var user User
if err := c.BodyParser(&user); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(201).JSON(user)
}适用场景:
- 团队熟悉Express.js模式
- 需要开箱即用的WebSocket支持
- 构建实时应用
- 想要最快的路由匹配性能
优势:
- 受Express.js启发的API(对Node.js开发者友好)
- 最快的路由匹配(使用fasthttp)
- 内置WebSocket支持
- 支持模板引擎
- 处理文件上传
局限性:
- 使用fasthttp而非net/http(生态系统兼容性较差)
- 不兼容标准http.Handler接口
- 生态系统稍不成熟
安装:
bash
go get -u github.com/gofiber/fiber/v2示例:
go
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/cors"
)
func main() {
app := fiber.New()
// Middleware
app.Use(logger.New())
app.Use(cors.New())
// Routes
api := app.Group("/api/v1")
users := api.Group("/users")
users.Get("/", listUsers)
users.Post("/", createUser)
users.Get("/:id", getUser)
users.Put("/:id", updateUser)
users.Delete("/:id", deleteUser)
app.Listen(":8080")
}
func getUser(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{
"id": id,
"name": "John Doe",
})
}
func createUser(c *fiber.Ctx) error {
var user User
if err := c.BodyParser(&user); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(201).JSON(user)
}Common HTTP Patterns
常见HTTP模式
Request Validation
请求验证
Struct Tag Validation:
go
import "github.com/go-playground/validator/v10"
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=3,max=50"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,gte=18,lte=120"`
Password string `json:"password" validate:"required,min=8"`
}
var validate = validator.New()
func validateRequest(req interface{}) error {
return validate.Struct(req)
}
// Usage
func createUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if err := validateRequest(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Process valid request
}Custom Validators:
go
import "github.com/go-playground/validator/v10"
var validate = validator.New()
func init() {
validate.RegisterValidation("username", validateUsername)
}
func validateUsername(fl validator.FieldLevel) bool {
username := fl.Field().String()
// Custom validation logic
return len(username) >= 3 && isAlphanumeric(username)
}
type SignupRequest struct {
Username string `validate:"required,username"`
Email string `validate:"required,email"`
}结构体标签验证:
go
import "github.com/go-playground/validator/v10"
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=3,max=50"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,gte=18,lte=120"`
Password string `json:"password" validate:"required,min=8"`
}
var validate = validator.New()
func validateRequest(req interface{}) error {
return validate.Struct(req)
}
// Usage
func createUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if err := validateRequest(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Process valid request
}自定义验证器:
go
import "github.com/go-playground/validator/v10"
var validate = validator.New()
func init() {
validate.RegisterValidation("username", validateUsername)
}
func validateUsername(fl validator.FieldLevel) bool {
username := fl.Field().String()
// Custom validation logic
return len(username) >= 3 && isAlphanumeric(username)
}
type SignupRequest struct {
Username string `validate:"required,username"`
Email string `validate:"required,email"`
}Middleware Patterns
中间件模式
Authentication Middleware:
go
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Validate token
userID, err := validateToken(token)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Add user to context
ctx := context.WithValue(r.Context(), "userID", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}Rate Limiting Middleware:
go
import (
"golang.org/x/time/rate"
"sync"
)
type RateLimiter struct {
limiters map[string]*rate.Limiter
mu sync.RWMutex
rate rate.Limit
burst int
}
func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
return &RateLimiter{
limiters: make(map[string]*rate.Limiter),
rate: r,
burst: b,
}
}
func (rl *RateLimiter) getLimiter(ip string) *rate.Limiter {
rl.mu.Lock()
defer rl.mu.Unlock()
limiter, exists := rl.limiters[ip]
if !exists {
limiter = rate.NewLimiter(rl.rate, rl.burst)
rl.limiters[ip] = limiter
}
return limiter
}
func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
limiter := rl.getLimiter(ip)
if !limiter.Allow() {
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}CORS Middleware:
go
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}认证中间件:
go
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Validate token
userID, err := validateToken(token)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Add user to context
ctx := context.WithValue(r.Context(), "userID", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}限流中间件:
go
import (
"golang.org/x/time/rate"
"sync"
)
type RateLimiter struct {
limiters map[string]*rate.Limiter
mu sync.RWMutex
rate rate.Limit
burst int
}
func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
return &RateLimiter{
limiters: make(map[string]*rate.Limiter),
rate: r,
burst: b,
}
}
func (rl *RateLimiter) getLimiter(ip string) *rate.Limiter {
rl.mu.Lock()
defer rl.mu.Unlock()
limiter, exists := rl.limiters[ip]
if !exists {
limiter = rate.NewLimiter(rl.rate, rl.burst)
rl.limiters[ip] = limiter
}
return limiter
}
func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
limiter := rl.getLimiter(ip)
if !limiter.Allow() {
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}CORS中间件:
go
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}Error Handling Strategies
错误处理策略
Custom Error Types:
go
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func (e *APIError) Error() string {
return e.Message
}
// Error constructors
func NewBadRequestError(msg string) *APIError {
return &APIError{Code: http.StatusBadRequest, Message: msg}
}
func NewNotFoundError(msg string) *APIError {
return &APIError{Code: http.StatusNotFound, Message: msg}
}
func NewInternalError(msg string) *APIError {
return &APIError{Code: http.StatusInternalServerError, Message: msg}
}Error Response Middleware:
go
type APIHandler func(w http.ResponseWriter, r *http.Request) error
func ErrorHandlerMiddleware(h APIHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := h(w, r)
if err == nil {
return
}
// Handle different error types
var apiErr *APIError
if errors.As(err, &apiErr) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(apiErr.Code)
json.NewEncoder(w).Encode(apiErr)
return
}
// Unknown error - log and return generic message
log.Printf("Internal error: %v", err)
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(APIError{
Code: http.StatusInternalServerError,
Message: "Internal server error",
})
})
}
// Usage
func getUserHandler(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
user, err := db.GetUser(id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return NewNotFoundError("User not found")
}
return fmt.Errorf("database error: %w", err)
}
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(user)
}
// Register with middleware
mux.Handle("GET /users/{id}", ErrorHandlerMiddleware(getUserHandler))自定义错误类型:
go
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func (e *APIError) Error() string {
return e.Message
}
// Error constructors
func NewBadRequestError(msg string) *APIError {
return &APIError{Code: http.StatusBadRequest, Message: msg}
}
func NewNotFoundError(msg string) *APIError {
return &APIError{Code: http.StatusNotFound, Message: msg}
}
func NewInternalError(msg string) *APIError {
return &APIError{Code: http.StatusInternalServerError, Message: msg}
}错误响应中间件:
go
type APIHandler func(w http.ResponseWriter, r *http.Request) error
func ErrorHandlerMiddleware(h APIHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := h(w, r)
if err == nil {
return
}
// Handle different error types
var apiErr *APIError
if errors.As(err, &apiErr) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(apiErr.Code)
json.NewEncoder(w).Encode(apiErr)
return
}
// Unknown error - log and return generic message
log.Printf("Internal error: %v", err)
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(APIError{
Code: http.StatusInternalServerError,
Message: "Internal server error",
})
})
}
// Usage
func getUserHandler(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
user, err := db.GetUser(id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return NewNotFoundError("User not found")
}
return fmt.Errorf("database error: %w", err)
}
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(user)
}
// Register with middleware
mux.Handle("GET /users/{id}", ErrorHandlerMiddleware(getUserHandler))REST API Design Patterns
REST API设计模式
Resource Naming Conventions:
go
// Good: Plural nouns for collections
GET /api/v1/users // List users
POST /api/v1/users // Create user
GET /api/v1/users/{id} // Get user
PUT /api/v1/users/{id} // Update user (full)
PATCH /api/v1/users/{id} // Update user (partial)
DELETE /api/v1/users/{id} // Delete user
// Nested resources
GET /api/v1/users/{id}/posts // User's posts
POST /api/v1/users/{id}/posts // Create post for user
GET /api/v1/users/{id}/posts/{pid} // Specific post
// Avoid: Verbs in URLs (use HTTP methods instead)
// Bad: POST /api/v1/users/create
// Bad: GET /api/v1/users/get/{id}Pagination Pattern:
go
type PaginationParams struct {
Page int `json:"page" validate:"gte=1"`
PageSize int `json:"page_size" validate:"gte=1,lte=100"`
}
type PaginatedResponse struct {
Data interface{} `json:"data"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalCount int `json:"total_count"`
TotalPages int `json:"total_pages"`
}
func listUsers(w http.ResponseWriter, r *http.Request) {
// Parse pagination params
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
page = 1
}
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
// Fetch data
users, totalCount := db.GetUsers(page, pageSize)
totalPages := (totalCount + pageSize - 1) / pageSize
response := PaginatedResponse{
Data: users,
Page: page,
PageSize: pageSize,
TotalCount: totalCount,
TotalPages: totalPages,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}Query Parameter Filtering:
go
type UserFilter struct {
Status string `json:"status"`
Role string `json:"role"`
CreatedAt string `json:"created_at"`
Search string `json:"search"`
}
func parseFilters(r *http.Request) UserFilter {
return UserFilter{
Status: r.URL.Query().Get("status"),
Role: r.URL.Query().Get("role"),
CreatedAt: r.URL.Query().Get("created_at"),
Search: r.URL.Query().Get("search"),
}
}
func listUsers(w http.ResponseWriter, r *http.Request) {
filters := parseFilters(r)
users := db.GetUsersWithFilters(filters)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// Example request: GET /api/v1/users?status=active&role=admin&search=john资源命名规范:
go
// 推荐:集合使用复数名词
GET /api/v1/users // 列出用户
POST /api/v1/users // 创建用户
GET /api/v1/users/{id} // 获取用户
PUT /api/v1/users/{id} // 全量更新用户
PATCH /api/v1/users/{id} // 部分更新用户
DELETE /api/v1/users/{id} // 删除用户
// 嵌套资源
GET /api/v1/users/{id}/posts // 用户的文章
POST /api/v1/users/{id}/posts // 为用户创建文章
GET /api/v1/users/{id}/posts/{pid} // 获取指定文章
// 避免:URL中使用动词(改用HTTP方法)
// 不推荐:POST /api/v1/users/create
// 不推荐:GET /api/v1/users/get/{id}分页模式:
go
type PaginationParams struct {
Page int `json:"page" validate:"gte=1"`
PageSize int `json:"page_size" validate:"gte=1,lte=100"`
}
type PaginatedResponse struct {
Data interface{} `json:"data"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalCount int `json:"total_count"`
TotalPages int `json:"total_pages"`
}
func listUsers(w http.ResponseWriter, r *http.Request) {
// 解析分页参数
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
page = 1
}
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
// 获取数据
users, totalCount := db.GetUsers(page, pageSize)
totalPages := (totalCount + pageSize - 1) / pageSize
response := PaginatedResponse{
Data: users,
Page: page,
PageSize: pageSize,
TotalCount: totalCount,
TotalPages: totalPages,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}查询参数过滤:
go
type UserFilter struct {
Status string `json:"status"`
Role string `json:"role"`
CreatedAt string `json:"created_at"`
Search string `json:"search"`
}
func parseFilters(r *http.Request) UserFilter {
return UserFilter{
Status: r.URL.Query().Get("status"),
Role: r.URL.Query().Get("role"),
CreatedAt: r.URL.Query().Get("created_at"),
Search: r.URL.Query().Get("search"),
}
}
func listUsers(w http.ResponseWriter, r *http.Request) {
filters := parseFilters(r)
users := db.GetUsersWithFilters(filters)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// 请求示例:GET /api/v1/users?status=active&role=admin&search=johnHTTP Client Patterns
HTTP客户端模式
Production-Ready HTTP Client:
go
import (
"context"
"net/http"
"time"
)
func NewHTTPClient() *http.Client {
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
DisableKeepAlives: false,
},
}
}
// Making requests with context
func fetchUser(ctx context.Context, userID string) (*User, error) {
client := NewHTTPClient()
req, err := http.NewRequestWithContext(
ctx,
"GET",
fmt.Sprintf("https://api.example.com/users/%s", userID),
nil,
)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+getToken())
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("execute request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return nil, fmt.Errorf("decode response: %w", err)
}
return &user, nil
}Retry Logic with Exponential Backoff:
go
import (
"context"
"math"
"time"
)
type RetryConfig struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
}
func DoWithRetry(ctx context.Context, cfg RetryConfig, fn func() error) error {
var err error
for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
err = fn()
if err == nil {
return nil
}
if attempt < cfg.MaxRetries {
// Exponential backoff
delay := time.Duration(math.Pow(2, float64(attempt))) * cfg.BaseDelay
if delay > cfg.MaxDelay {
delay = cfg.MaxDelay
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
// Continue to next attempt
}
}
}
return fmt.Errorf("max retries exceeded: %w", err)
}
// Usage
err := DoWithRetry(ctx, RetryConfig{
MaxRetries: 3,
BaseDelay: 100 * time.Millisecond,
MaxDelay: 2 * time.Second,
}, func() error {
return makeAPIRequest()
})生产级HTTP客户端:
go
import (
"context"
"net/http"
"time"
)
func NewHTTPClient() *http.Client {
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
DisableKeepAlives: false,
},
}
}
// 使用上下文发起请求
func fetchUser(ctx context.Context, userID string) (*User, error) {
client := NewHTTPClient()
req, err := http.NewRequestWithContext(
ctx,
"GET",
fmt.Sprintf("https://api.example.com/users/%s", userID),
nil,
)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+getToken())
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("execute request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return nil, fmt.Errorf("decode response: %w", err)
}
return &user, nil
}带指数退避的重试逻辑:
go
import (
"context"
"math"
"time"
)
type RetryConfig struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
}
func DoWithRetry(ctx context.Context, cfg RetryConfig, fn func() error) error {
var err error
for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
err = fn()
if err == nil {
return nil
}
if attempt < cfg.MaxRetries {
// 指数退避
delay := time.Duration(math.Pow(2, float64(attempt))) * cfg.BaseDelay
if delay > cfg.MaxDelay {
delay = cfg.MaxDelay
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
// 继续下一次尝试
}
}
}
return fmt.Errorf("max retries exceeded: %w", err)
}
// 使用示例
err := DoWithRetry(ctx, RetryConfig{
MaxRetries: 3,
BaseDelay: 100 * time.Millisecond,
MaxDelay: 2 * time.Second,
}, func() error {
return makeAPIRequest()
})Testing HTTP Handlers
测试HTTP处理器
Using httptest.Server:
go
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestGetUser(t *testing.T) {
// Create test server
ts := httptest.NewServer(http.HandlerFunc(getUserHandler))
defer ts.Close()
// Make request
resp, err := http.Get(ts.URL + "/users/123")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
// Assertions
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", resp.StatusCode)
}
var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
t.Fatal(err)
}
if user.ID != "123" {
t.Errorf("expected user ID 123, got %s", user.ID)
}
}Testing Middleware:
go
func TestAuthMiddleware(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID")
if userID == nil {
t.Error("userID not found in context")
}
w.WriteHeader(http.StatusOK)
})
wrapped := AuthMiddleware(handler)
tests := []struct {
name string
token string
wantStatus int
}{
{"Valid token", "valid-token-123", http.StatusOK},
{"Missing token", "", http.StatusUnauthorized},
{"Invalid token", "invalid", http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/test", nil)
if tt.token != "" {
req.Header.Set("Authorization", tt.token)
}
rr := httptest.NewRecorder()
wrapped.ServeHTTP(rr, req)
if rr.Code != tt.wantStatus {
t.Errorf("expected status %d, got %d", tt.wantStatus, rr.Code)
}
})
}
}使用httptest.Server:
go
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestGetUser(t *testing.T) {
// 创建测试服务器
ts := httptest.NewServer(http.HandlerFunc(getUserHandler))
defer ts.Close()
// 发起请求
resp, err := http.Get(ts.URL + "/users/123")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
// 断言
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", resp.StatusCode)
}
var user User
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
t.Fatal(err)
}
if user.ID != "123" {
t.Errorf("expected user ID 123, got %s", user.ID)
}
}测试中间件:
go
func TestAuthMiddleware(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID")
if userID == nil {
t.Error("userID not found in context")
}
w.WriteHeader(http.StatusOK)
})
wrapped := AuthMiddleware(handler)
tests := []struct {
name string
token string
wantStatus int
}{
{"Valid token", "valid-token-123", http.StatusOK},
{"Missing token", "", http.StatusUnauthorized},
{"Invalid token", "invalid", http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/test", nil)
if tt.token != "" {
req.Header.Set("Authorization", tt.token)
}
rr := httptest.NewRecorder()
wrapped.ServeHTTP(rr, req)
if rr.Code != tt.wantStatus {
t.Errorf("expected status %d, got %d", tt.wantStatus, rr.Code)
}
})
}
}Performance Optimization
性能优化
Response Compression:
go
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func GzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzw, r)
})
}Connection Pooling Configuration:
go
import (
"net"
"net/http"
"time"
)
func NewProductionHTTPClient() *http.Client {
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
}响应压缩:
go
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func GzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzw, r)
})
}连接池配置:
go
import (
"net"
"net/http"
"time"
)
func NewProductionHTTPClient() *http.Client {
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
}Decision Tree
决策树
Building HTTP API in Go?
│
├─ Simple API, few dependencies? → net/http (stdlib)
│ ├─ Go 1.22+? → Use new ServeMux pattern routing
│ └─ Go < 1.22? → Consider Chi for better routing
│
├─ Need stdlib compatibility + better ergonomics? → Chi
│ └─ Great for: Middleware chains, route grouping
│
├─ Maximum performance priority? → Gin
│ └─ Great for: JSON APIs, high throughput services
│
├─ Enterprise app with OpenAPI? → Echo
│ └─ Great for: Type safety, comprehensive middleware
│
└─ Team knows Express.js + need WebSockets? → Fiber
└─ Note: Uses fasthttp, not stdlib-compatible要在Go中构建HTTP API?
│
├─ 简单API,依赖少? → net/http(标准库)
│ ├─ 使用Go 1.22+? → 使用新的ServeMux模式路由
│ └─ 使用Go < 1.22? → 考虑使用Chi获得更好的路由能力
│
├─ 需要兼容标准库且更易用? → Chi
│ └─ 适用场景:中间件链、路由分组
│
├─ 优先考虑极致性能? → Gin
│ └─ 适用场景:JSON API、高吞吐量服务
│
├─ 企业级应用且需要OpenAPI? → Echo
│ └─ 适用场景:类型安全、全面的中间件
│
└─ 团队熟悉Express.js且需要WebSocket? → Fiber
└─ 注意:使用fasthttp,不兼容标准库Common Pitfalls
常见陷阱
Pitfall 1: Not Closing Response Bodies
go
// Bad: Memory leak
resp, _ := http.Get(url)
body, _ := io.ReadAll(resp.Body) // Never closed!
// Good: Always defer close
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)Pitfall 2: Not Using Context for Timeouts
go
// Bad: No timeout control
resp, _ := http.Get(url)
// Good: Use context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)Pitfall 3: Ignoring HTTP Status Codes
go
// Bad: Not checking status
resp, _ := http.Get(url)
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&result)
// Good: Always check status
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}Pitfall 4: Not Reusing HTTP Clients
go
// Bad: Creates new client per request (no connection pooling)
func makeRequest() {
client := &http.Client{}
resp, _ := client.Get(url)
}
// Good: Reuse client for connection pooling
var httpClient = &http.Client{
Timeout: 30 * time.Second,
}
func makeRequest() {
resp, _ := httpClient.Get(url)
}陷阱1:未关闭响应体
go
// 错误:内存泄漏
resp, _ := http.Get(url)
body, _ := io.ReadAll(resp.Body) // 从未关闭!
// 正确:始终延迟关闭
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)陷阱2:未使用上下文设置超时
go
// 错误:无超时控制
resp, _ := http.Get(url)
// 正确:使用带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)陷阱3:忽略HTTP状态码
go
// 错误:未检查状态码
resp, _ := http.Get(url)
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&result)
// 正确:始终检查状态码
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}陷阱4:未复用HTTP客户端
go
// 错误:每次请求创建新客户端(无连接池)
func makeRequest() {
client := &http.Client{}
resp, _ := client.Get(url)
}
// 正确:复用客户端以实现连接池
var httpClient = &http.Client{
Timeout: 30 * time.Second,
}
func makeRequest() {
resp, _ := httpClient.Get(url)
}Related Skills
相关技能
- golang-testing-strategies: Testing HTTP handlers and middleware
- golang-database-patterns: Integrating databases with HTTP APIs
- toolchains-typescript-frameworks-nodejs-backend: Comparison with Node.js patterns
- golang-testing-strategies:测试HTTP处理器和中间件
- golang-database-patterns:将数据库与HTTP API集成
- toolchains-typescript-frameworks-nodejs-backend:与Node.js模式对比