gin + es 实践 04

打印 上一主题 下一主题

主题 1983|帖子 1983|积分 5949

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
API 接口筹划

Go-ES 项目提供了一套完整的 RESTful API,用于产品管理和搜索。本文档详细先容了 API 的筹划原则、接口定义和使用方法。
API 筹划原则

本项目标 API 筹划遵照以下原则:

  • RESTful 风格:使用标准的 HTTP 方法体现操作类型
  • 资源导向:API 路径以资源名词为中央
  • JSON 格式:请求和相应均使用 JSON 格式
  • 版本控制:API 路径包含版本号
  • 统一错误处理:使用一致的错误相应格式
  • 自文档化:使用 Swagger 注解自动天生 API 文档
API 底子信息



  • 底子路径:/api/v1
  • 内容类型:application/json
  • 认证方式:API Key(通过 Authorization 头)
API 端点概览

方法路径描述POST/products创建产品GET/products获取产品列表GET/products/{id}获取产品详情PUT/products/{id}更新产品DELETE/products/{id}删除产品GET/products/category/{category}获取指定种别产品GET/products/search搜索产品POST/products/reindex重修产品索引 API 详细分析

1. 创建产品

请求:
  1. POST /api/v1/products
  2. Content-Type: application/json
  3. {
  4.   "name": "示例产品",
  5.   "description": "这是一个示例产品",
  6.   "price": 99.99,
  7.   "category": "电子产品",
  8.   "tags": ["新品", "热销"]
  9. }
复制代码
相应:
  1. HTTP/1.1 201 Created
  2. Content-Type: application/json
  3. {
  4.   "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  5.   "name": "示例产品",
  6.   "description": "这是一个示例产品",
  7.   "price": 99.99,
  8.   "category": "电子产品",
  9.   "tags": ["新品", "热销"],
  10.   "created_at": "2023-05-01T12:00:00Z",
  11.   "updated_at": "2023-05-01T12:00:00Z"
  12. }
复制代码
处理逻辑:
  1. func (h *ProductHandler) Create(c *gin.Context) {
  2.     var req dto.CreateProductRequest
  3.     if err := c.ShouldBindJSON(&req); err != nil {
  4.         c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求数据"})
  5.         return
  6.     }
  7.     product, err := h.productAppService.CreateProduct(c.Request.Context(), &req)
  8.     if err != nil {
  9.         c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("创建产品失败: %v", err)})
  10.         return
  11.     }
  12.     c.JSON(http.StatusCreated, product)
  13. }
复制代码
2. 获取产品列表

请求:
  1. GET /api/v1/products?page=1&size=10
复制代码
相应:
  1. HTTP/1.1 200 OK
  2. Content-Type: application/json
  3. {
  4.   "total": 100,
  5.   "page": 1,
  6.   "page_size": 10,
  7.   "products": [
  8.     {
  9.       "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  10.       "name": "示例产品",
  11.       "description": "这是一个示例产品",
  12.       "price": 99.99,
  13.       "category": "电子产品",
  14.       "tags": ["新品", "热销"],
  15.       "created_at": "2023-05-01T12:00:00Z",
  16.       "updated_at": "2023-05-01T12:00:00Z"
  17.     },
  18.     // ... 更多产品
  19.   ]
  20. }
复制代码
处理逻辑:
  1. func (h *ProductHandler) List(c *gin.Context) {
  2.     page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
  3.     size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
  4.     products, err := h.productAppService.ListProducts(c.Request.Context(), page, size)
  5.     if err != nil {
  6.         c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("获取产品列表失败: %v", err)})
  7.         return
  8.     }
  9.     c.JSON(http.StatusOK, products)
  10. }
复制代码
3. 获取产品详情

请求:
  1. GET /api/v1/products/f47ac10b-58cc-4372-a567-0e02b2c3d479
复制代码
相应:
  1. HTTP/1.1 200 OK
  2. Content-Type: application/json
  3. {
  4.   "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  5.   "name": "示例产品",
  6.   "description": "这是一个示例产品",
  7.   "price": 99.99,
  8.   "category": "电子产品",
  9.   "tags": ["新品", "热销"],
  10.   "created_at": "2023-05-01T12:00:00Z",
  11.   "updated_at": "2023-05-01T12:00:00Z"
  12. }
复制代码
处理逻辑:
  1. func (h *ProductHandler) Get(c *gin.Context) {
  2.     id := c.Param("id")
  3.     if id == "" {
  4.         c.JSON(http.StatusBadRequest, gin.H{"error": "产品ID不能为空"})
  5.         return
  6.     }
  7.     product, err := h.productAppService.GetProduct(c.Request.Context(), id)
  8.     if err != nil {
  9.         if err.Error() == "产品不存在" {
  10.             c.JSON(http.StatusNotFound, gin.H{"error": "产品不存在"})
  11.             return
  12.         }
  13.         c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("获取产品失败: %v", err)})
  14.         return
  15.     }
  16.     c.JSON(http.StatusOK, product)
  17. }
复制代码
4. 搜索产品

请求:
  1. GET /api/v1/products/search?q=手机&category=电子产品&page=1&size=10
复制代码
相应:
  1. HTTP/1.1 200 OK
  2. Content-Type: application/json
  3. {
  4.   "total": 5,
  5.   "page": 1,
  6.   "page_size": 10,
  7.   "products": [
  8.     {
  9.       "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  10.       "name": "高端智能手机",
  11.       "description": "这是一款功能强大的智能手机",
  12.       "price": 3999.99,
  13.       "category": "电子产品",
  14.       "tags": ["手机", "智能设备"],
  15.       "created_at": "2023-05-01T12:00:00Z",
  16.       "updated_at": "2023-05-01T12:00:00Z"
  17.     },
  18.     // ... 更多产品
  19.   ]
  20. }
复制代码
处理逻辑:
  1. func (h *ProductHandler) Search(c *gin.Context) {
  2.     var searchReq dto.SearchProductRequest
  3.     if err := c.ShouldBindQuery(&searchReq); err != nil {
  4.         c.JSON(http.StatusBadRequest, gin.H{"error": "无效的搜索参数"})
  5.         return
  6.     }
  7.     products, err := h.productAppService.SearchProducts(c.Request.Context(), &searchReq)
  8.     if err != nil {
  9.         c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("搜索产品失败: %v", err)})
  10.         return
  11.     }
  12.     c.JSON(http.StatusOK, products)
  13. }
复制代码
请求与相应模型

请求模型

创建产品请求

  1. // CreateProductRequest 创建产品请求
  2. type CreateProductRequest struct {
  3.     Name        string   `json:"name" binding:"required"`
  4.     Description string   `json:"description"`
  5.     Price       float64  `json:"price" binding:"required,gt=0"`
  6.     Category    string   `json:"category" binding:"required"`
  7.     Tags        []string `json:"tags"`
  8. }
复制代码
更新产品请求

  1. // UpdateProductRequest 更新产品请求
  2. type UpdateProductRequest struct {
  3.     Name        string   `json:"name" binding:"required"`
  4.     Description string   `json:"description"`
  5.     Price       float64  `json:"price" binding:"required,gt=0"`
  6.     Category    string   `json:"category" binding:"required"`
  7.     Tags        []string `json:"tags"`
  8. }
复制代码
搜索产品请求

  1. // SearchProductRequest 搜索产品请求
  2. type SearchProductRequest struct {
  3.     Keyword  string `form:"q"`
  4.     Category string `form:"category"`
  5.     Page     int    `form:"page,default=1"`
  6.     PageSize int    `form:"size,default=10"`
  7. }
复制代码
相应模型

产品相应

  1. // ProductResponse 产品响应
  2. type ProductResponse struct {
  3.     ID          string    `json:"id"`
  4.     Name        string    `json:"name"`
  5.     Description string    `json:"description"`
  6.     Price       float64   `json:"price"`
  7.     Category    string    `json:"category"`
  8.     Tags        []string  `json:"tags"`
  9.     CreatedAt   time.Time `json:"created_at"`
  10.     UpdatedAt   time.Time `json:"updated_at"`
  11. }
复制代码
产品列表相应

  1. // ProductListResponse 产品列表响应
  2. type ProductListResponse struct {
  3.     Total    int64            `json:"total"`
  4.     Page     int              `json:"page"`
  5.     PageSize int              `json:"page_size"`
  6.     Products []ProductResponse `json:"products"`
  7. }
复制代码
错误处理

API 使用统一的错误相应格式:
  1. {
  2.   "error": "错误描述信息"
  3. }
复制代码
常见的错误相应包括:


  • 400 Bad Request: 请求参数无效
  • 404 Not Found: 请求的资源不存在
  • 500 Internal Server Error: 服务器内部错误
API 文档天生

本项目使用 Swagger 自动天生 API 文档。API 处理器中的注解如下所示:
  1. // Create 创建产品
  2. // @Summary 创建新产品
  3. // @Description 创建一个新的产品并索引到Elasticsearch
  4. // @Tags 产品
  5. // @Accept  json
  6. // @Produce  json
  7. // @Param product body dto.CreateProductRequest true "产品信息"
  8. // @Success 201 {object} dto.ProductResponse
  9. // @Failure 400 {object} map[string]string "错误信息"
  10. // @Failure 500 {object} map[string]string "错误信息"
  11. // @Router /products [post]
  12. func (h *ProductHandler) Create(c *gin.Context) {
  13.     // 实现逻辑...
  14. }
复制代码
使用 Swagger 文档


  • 启动应用后,访问 Swagger UI:
  1. http://localhost:8080/swagger/index.html
复制代码

  • 在 Swagger UI 界面中可以:

    • 欣赏所有 API 端点
    • 检察请求参数和相应格式
    • 直接在欣赏器中测试 API

API 路由注册

所有 API 路由在 interfaces/api/router/router.go 中注册:
  1. // SetupRouter 设置路由
  2. func SetupRouter(engine *gin.Engine, productHandler *handler.ProductHandler) {
  3.     // 设置中间件
  4.     engine.Use(gin.Logger())
  5.     engine.Use(gin.Recovery())
  6.     engine.Use(middleware.Cors())
  7.     // 添加Swagger
  8.     engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
  9.     // API 路由组
  10.     api := engine.Group("/api/v1")
  11.     {
  12.         // 产品相关路由
  13.         products := api.Group("/products")
  14.         {
  15.             products.POST("", productHandler.Create)
  16.             products.GET("", productHandler.List)
  17.             products.GET("/:id", productHandler.Get)
  18.             products.PUT("/:id", productHandler.Update)
  19.             products.DELETE("/:id", productHandler.Delete)
  20.             products.GET("/category/:category", productHandler.ListByCategory)
  21.             products.GET("/search", productHandler.Search)
  22.             products.POST("/reindex", productHandler.ReindexAll)
  23.         }
  24.     }
  25. }
复制代码
跨域处理

API 支持跨域请求,通过自定义中间件实现:
  1. // Cors 添加CORS头
  2. func Cors() gin.HandlerFunc {
  3.     return func(c *gin.Context) {
  4.         c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
  5.         c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
  6.         c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
  7.         c.Writer.Header().Set("Access-Control-Max-Age", "86400")
  8.         // 处理OPTIONS请求
  9.         if c.Request.Method == "OPTIONS" {
  10.             c.AbortWithStatus(200)
  11.             return
  12.         }
  13.         // 处理请求
  14.         c.Next()
  15.     }
  16. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

没腿的鸟

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表