Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:47:23 +08:00
commit eb42d25f7b
51 changed files with 9966 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
---
name: go-api-reviewer
description: |
WHEN: Go API review with Gin/Echo/Fiber/Chi, router patterns, middleware, request handling
WHAT: Router organization + Middleware patterns + Request validation + Error responses + OpenAPI
WHEN NOT: General Go → go-reviewer, Rust API → rust-api-reviewer
---
# Go API Reviewer Skill
## Purpose
Reviews Go API projects using Gin, Echo, Fiber, or Chi for routing, middleware, and API patterns.
## When to Use
- Go REST API code review
- Gin/Echo/Fiber/Chi project review
- Middleware implementation review
- API request/response handling
- API documentation review
## Project Detection
- `github.com/gin-gonic/gin` import
- `github.com/labstack/echo` import
- `github.com/gofiber/fiber` import
- `github.com/go-chi/chi` import
- `handlers/`, `routes/`, `middleware/` directories
## Workflow
### Step 1: Analyze Project
```
**Framework**: Gin v1.9+
**Router**: Group-based routing
**Middleware**: Auth, CORS, Logger, Recovery
**Validation**: go-playground/validator
**Docs**: Swagger/OpenAPI
```
### Step 2: Select Review Areas
**AskUserQuestion:**
```
"Which areas to review?"
Options:
- Full API review (recommended)
- Router and handler patterns
- Middleware implementation
- Request validation
- Error handling and responses
multiSelect: true
```
## Detection Rules
### Router Organization
| Check | Recommendation | Severity |
|-------|----------------|----------|
| All routes in main | Use router groups | MEDIUM |
| No versioning | Add /api/v1 prefix | MEDIUM |
| Inconsistent naming | Follow REST conventions | LOW |
| No route grouping | Group by resource | MEDIUM |
```go
// BAD: All routes in main.go
func main() {
r := gin.Default()
r.GET("/users", getUsers)
r.POST("/users", createUser)
r.GET("/users/:id", getUser)
r.GET("/products", getProducts)
// ... 50 more routes
}
// GOOD: Organized route groups (Gin)
func SetupRouter() *gin.Engine {
r := gin.Default()
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)
}
products := api.Group("/products")
{
products.GET("", listProducts)
products.GET("/:id", getProduct)
}
}
return r
}
// GOOD: Separate route files
// routes/users.go
func RegisterUserRoutes(rg *gin.RouterGroup) {
users := rg.Group("/users")
h := NewUserHandler()
users.GET("", h.List)
users.POST("", h.Create)
users.GET("/:id", h.Get)
}
```
### Middleware Patterns
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Auth in handler | Extract to middleware | HIGH |
| No recovery middleware | Add panic recovery | HIGH |
| No request ID | Add request ID middleware | MEDIUM |
| Middleware order wrong | Order: Logger → Recovery → Auth | MEDIUM |
```go
// GOOD: Middleware stack (Gin)
func SetupMiddleware(r *gin.Engine) {
// Order matters!
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(RequestIDMiddleware())
r.Use(CORSMiddleware())
}
// GOOD: Auth middleware
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
claims, err := ValidateToken(token)
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
return
}
c.Set("user_id", claims.UserID)
c.Next()
}
}
// Usage
api := r.Group("/api/v1")
api.Use(AuthMiddleware())
// GOOD: Request ID middleware
func RequestIDMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := c.GetHeader("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
c.Set("request_id", requestID)
c.Header("X-Request-ID", requestID)
c.Next()
}
}
```
### Request Validation
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Manual validation | Use validator tags | MEDIUM |
| No binding errors | Return validation errors | HIGH |
| No request DTOs | Define request structs | MEDIUM |
| Missing required fields | Add binding:"required" | HIGH |
```go
// GOOD: Request struct with validation
type CreateUserRequest struct {
Name string `json:"name" binding:"required,min=1,max=100"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"gte=0,lte=150"`
Role string `json:"role" binding:"oneof=admin user guest"`
Password string `json:"password" binding:"required,min=8"`
}
// GOOD: Handler with validation
func (h *UserHandler) Create(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{
"error": "validation_error",
"details": formatValidationErrors(err),
})
return
}
user, err := h.service.Create(c.Request.Context(), &req)
if err != nil {
handleError(c, err)
return
}
c.JSON(201, user)
}
// GOOD: Format validation errors
func formatValidationErrors(err error) map[string]string {
errors := make(map[string]string)
var ve validator.ValidationErrors
if errors.As(err, &ve) {
for _, e := range ve {
field := strings.ToLower(e.Field())
errors[field] = getErrorMessage(e)
}
}
return errors
}
```
### Error Handling
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Inconsistent error format | Use standard error response | HIGH |
| Internal errors exposed | Hide implementation details | HIGH |
| No error codes | Add error codes | MEDIUM |
| HTTP status inconsistent | Follow REST conventions | MEDIUM |
```go
// GOOD: Standard error response
type ErrorResponse struct {
Error string `json:"error"`
Code string `json:"code,omitempty"`
Details map[string]string `json:"details,omitempty"`
}
// GOOD: Custom errors
var (
ErrNotFound = &AppError{Code: "NOT_FOUND", Status: 404}
ErrUnauthorized = &AppError{Code: "UNAUTHORIZED", Status: 401}
ErrConflict = &AppError{Code: "CONFLICT", Status: 409}
)
type AppError struct {
Code string
Status int
Message string
}
func (e *AppError) Error() string {
return e.Message
}
// GOOD: Error handler
func handleError(c *gin.Context, err error) {
var appErr *AppError
if errors.As(err, &appErr) {
c.JSON(appErr.Status, ErrorResponse{
Error: appErr.Message,
Code: appErr.Code,
})
return
}
// Log internal error, return generic message
log.Printf("internal error: %v", err)
c.JSON(500, ErrorResponse{
Error: "Internal server error",
Code: "INTERNAL_ERROR",
})
}
```
### Framework-Specific (Echo)
```go
// Echo example
func SetupEcho() *echo.Echo {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.RequestID())
e.Use(middleware.CORS())
api := e.Group("/api/v1")
api.Use(AuthMiddleware)
RegisterUserRoutes(api)
return e
}
// Echo handler
func (h *UserHandler) Create(c echo.Context) error {
var req CreateUserRequest
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(400, "invalid request")
}
if err := c.Validate(&req); err != nil {
return err
}
user, err := h.service.Create(c.Request().Context(), &req)
if err != nil {
return err
}
return c.JSON(201, user)
}
```
## Response Template
```
## Go API Code Review Results
**Project**: [name]
**Framework**: Gin 1.9 | **Go**: 1.22
### Router Organization
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | main.go | 40+ routes in single file |
### Middleware
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/user.go | Auth check in handler, not middleware |
### Validation
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/user.go:34 | No request validation |
### Error Handling
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/product.go | Inconsistent error response format |
### Recommended Actions
1. [ ] Split routes into separate files by resource
2. [ ] Extract auth logic to middleware
3. [ ] Add request struct validation
4. [ ] Implement standard error response format
```
## Best Practices
1. **Router**: Group by resource, version API
2. **Middleware**: Proper order, reusable
3. **Validation**: Use validator tags
4. **Errors**: Standard format, hide internals
5. **Docs**: Generate OpenAPI from code
## Integration
- `go-reviewer`: General Go patterns
- `security-scanner`: API security
- `api-documenter`: OpenAPI documentation