协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
gin【开发与工程】Gin is a Go web framework with a Martini-like API and a zero-allocation router. Use it to build HTTP APIs that need routing, middleware, request binding/validation, JSON responses, and graceful shutdown with low per-request overhead. Use when a user asks to scaffold a Go HTTP API, add route groups or middleware, validate request bodies, or shut a Go …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Gin is a minimal, fast HTTP web framework for Go built on net/http with a radix-tree router (originally based on httprouter). It provides routing, route groups, middleware chaining, request binding/validation via go-playground/validator, and JSON/XML/YAML rendering helpers. It is unopinionated about project layout and data access — you wire in your own database and auth logic.
go get github.com/gin-gonic/gin
This adds github.com/gin-gonic/gin to go.mod. Gin's own go.mod currently requires Go 1.26 to build from source; a project that only imports Gin as a dependency needs a Go toolchain at least that new to resolve the module.
gin.Default() returns an engine with the Logger and Recovery middleware attached; gin.New() returns a bare engine.r.Group("/api") creates a route group; call .Use(middleware) on the group to scope middleware to it.func(*gin.Context); call c.Next() to continue the chain or c.Abort()/c.AbortWithStatusJSON(...) to stop it.c.ShouldBindJSON(&v) parses and validates the request body against struct binding tags, returning an error instead of aborting — check it yourself.GIN_MODE=release (or gin.SetMode(gin.ReleaseMode)) in production; debug mode logs every route registration and is slower.Gin's own r.Run(":8080") blocks forever and has no shutdown hook, so production services run http.Server{Handler: r} directly and call Shutdown(ctx) on a signal, as in Example 2 below.
/api/v1 group with JWT auth and request validation"package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type CreateOrderRequest struct {
ProductSKU string `json:"productSku" binding:"required"`
Quantity int `json:"quantity" binding:"required,min=1"`
}
func main() {
r := gin.Default() // Logger + Recovery middleware
v1 := r.Group("/api/v1")
v1.Use(authMiddleware())
{
v1.GET("/orders", listOrders)
v1.POST("/orders", createOrder)
v1.GET("/orders/:id", getOrder)
}
r.Run(":8080") // fine for local dev; see Example 2 for production
}
func listOrders(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
orders, total := store.FindOrders(page, limit)
c.JSON(http.StatusOK, gin.H{"data": orders, "total": total, "page": page})
}
func createOrder(c *gin.Context) {
var req CreateOrderRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
order, err := store.CreateOrder(req.ProductSKU, req.Quantity)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "could not create order"})
return
}
c.JSON(http.StatusCreated, order)
}
func getOrder(c *gin.Context) {
order, err := store.FindOrder(c.Param("id"))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "order not found"})
return
}
c.JSON(http.StatusOK, order)
}
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing token"})
return
}
user, err := validateJWT(token)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
return
}
c.Set("user", user)
c.Next()
}
}
Result: POST /api/v1/orders without a token returns 401 {"error":"missing token"}; with a valid token and a body missing quantity it returns 400 naming the failed validation; a valid request returns 201 with the created order.
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/healthz", func(c *gin.Context) { c.Status(http.StatusOK) })
srv := &http.Server{Addr: ":8080", Handler: r}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("forced shutdown: %v", err)
}
}
Result: on SIGTERM, the process stops accepting new connections and waits up to 5 seconds for in-flight requests to finish before exiting, instead of killing them mid-response.
ShouldBindJSON/ShouldBindQuery over the panicking BindJSON/BindQuery variants — the Should* forms return an error you handle, while the non-Should forms abort the request with a 400 for you, which is easy to forget and double-handle.gin.H{} is a map[string]any alias — fine for ad-hoc responses, but use typed structs for anything with a documented response shape (OpenAPI, client codegen).c.Next() returns, so put logging/recovery first and auth after CORS.GIN_MODE=release for production builds.r.Run() has no graceful shutdown; use it only for local development, and switch to an explicit http.Server plus Shutdown(ctx) for anything deployed.