Files
go-common/docs/http.md

14 KiB
Raw Permalink Blame History

HTTP Restful工具文档

概述

HTTP Restful工具提供了标准化的HTTP请求和响应处理功能采用Handler黑盒模式封装了ResponseWriterRequest提供简洁的API无需每次都传递这两个参数。

功能特性

  • 黑盒模式:封装ResponseWriterRequest提供简洁的API
  • 标准化的响应结构{code, message, timestamp, data}
  • 分离HTTP状态码和业务状态码
  • 支持分页响应
  • 提供便捷的请求参数解析方法
  • 支持JSON请求体解析

响应结构

标准响应结构

{
    "code": 0,
    "message": "success",
    "timestamp": 1704067200,
    "data": {}
}

分页响应结构

{
    "code": 0,
    "message": "success",
    "timestamp": 1704067200,
    "data": {
        "list": [],
        "total": 100,
        "page": 1,
        "pageSize": 10
    }
}

使用方法

1. 创建Handler

import (
    "net/http"
    commonhttp "git.toowon.com/jimmy/go-common/http"
)

// 方式1使用HandleFunc包装器推荐最简洁
func GetUser(h *commonhttp.Handler) {
    id := h.GetQueryInt64("id", 0)
    h.Success(data)
}

http.HandleFunc("/user", commonhttp.HandleFunc(GetUser))

// 方式2手动创建Handler需要更多控制时
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
    h := commonhttp.NewHandler(w, r)
    GetUser(h)
})

2. 成功响应

func handler(h *commonhttp.Handler) {
    // 简单成功响应data为nil
    h.Success(nil)
    
    // 带数据的成功响应
    data := map[string]interface{}{
        "id": 1,
        "name": "test",
    }
    h.Success(data)
    
    // 带消息的成功响应
    h.SuccessWithMessage("操作成功", data)
}

3. 错误响应

func handler(h *commonhttp.Handler) {
    // 业务错误HTTP 200业务code非0
    h.Error(1001, "用户不存在")
    
    // 系统错误HTTP 500
    h.SystemError("服务器内部错误")
    
    // 其他HTTP错误状态码使用WriteJSON直接指定
    // 请求错误HTTP 400
    h.WriteJSON(http.StatusBadRequest, 400, "请求参数错误", nil)
    
    // 未授权HTTP 401
    h.WriteJSON(http.StatusUnauthorized, 401, "未登录", nil)
    
    // 禁止访问HTTP 403
    h.WriteJSON(http.StatusForbidden, 403, "无权限访问", nil)
    
    // 未找到HTTP 404
    h.WriteJSON(http.StatusNotFound, 404, "资源不存在", nil)
}

4. 分页响应

func handler(h *commonhttp.Handler) {
    // 获取分页参数
    pagination := h.ParsePaginationRequest()
    page := pagination.GetPage()
    pageSize := pagination.GetSize()
    
    // 查询数据(示例)
    list, total := getDataList(page, pageSize)
    
    // 返回分页响应(使用默认消息)
    h.SuccessPage(list, total, page, pageSize)
    
    // 返回分页响应(自定义消息)
    h.SuccessPage(list, total, page, pageSize, "查询成功")
}

5. 解析请求

解析JSON请求体

func handler(h *commonhttp.Handler) {
    type CreateUserRequest struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }
    
    var req CreateUserRequest
    if err := h.ParseJSON(&req); err != nil {
        h.WriteJSON(http.StatusBadRequest, 400, "请求参数解析失败", nil)
        return
    }
    
    // 使用req...
}

获取查询参数

func handler(h *commonhttp.Handler) {
    // 获取字符串参数
    name := h.GetQuery("name", "")
    email := h.GetQuery("email", "default@example.com")
    
    // 获取整数参数
    id := h.GetQueryInt("id", 0)
    age := h.GetQueryInt("age", 18)
    
    // 获取int64参数
    userId := h.GetQueryInt64("userId", 0)
    
    // 获取布尔参数
    isActive := h.GetQueryBool("isActive", false)
    
    // 获取浮点数参数
    price := h.GetQueryFloat64("price", 0.0)
}

获取表单参数

func handler(h *commonhttp.Handler) {
    // 获取表单字符串
    name := h.GetFormValue("name", "")
    
    // 获取表单整数
    age := h.GetFormInt("age", 0)
    
    // 获取表单int64
    userId := h.GetFormInt64("userId", 0)
    
    // 获取表单布尔值
    isActive := h.GetFormBool("isActive", false)
}

获取请求头

func handler(h *commonhttp.Handler) {
    token := h.GetHeader("Authorization", "")
    contentType := h.GetHeader("Content-Type", "application/json")
}

获取分页参数

方式1使用 PaginationRequest 结构(推荐)

func handler(h *commonhttp.Handler) {
    // 定义请求结构(包含分页字段)
    type ListUserRequest struct {
        Keyword string `json:"keyword"`
        commonhttp.PaginationRequest // 嵌入分页请求结构
    }
    
    // 从JSON请求体解析分页字段会自动解析
    var req ListUserRequest
    if err := h.ParseJSON(&req); err != nil {
        h.WriteJSON(http.StatusBadRequest, 400, "请求参数解析失败", nil)
        return
    }
    
    // 使用分页方法
    page := req.GetPage()      // 获取页码默认1
    size := req.GetSize()       // 获取每页数量默认20最大100优先使用page_size
    offset := req.GetOffset()   // 计算偏移量
}

// 或者从查询参数/form解析分页
func handler(h *commonhttp.Handler) {
    pagination := h.ParsePaginationRequest()
    page := pagination.GetPage()
    size := pagination.GetSize()
    offset := pagination.GetOffset()
}

获取时区

func handler(h *commonhttp.Handler) {
    // 从请求的context中获取时区
    // 如果使用了middleware.Timezone中间件可以从context中获取时区信息
    // 如果未设置,返回默认时区 AsiaShanghai
    timezone := h.GetTimezone()
}

6. 访问原始对象

如果需要访问原始的ResponseWriterRequest

func handler(h *commonhttp.Handler) {
    // 获取原始ResponseWriter
    w := h.ResponseWriter()
    
    // 获取原始Request
    r := h.Request()
    
    // 获取Context
    ctx := h.Context()
}

完整示例

package main

import (
    "log"
    "net/http"
    commonhttp "git.toowon.com/jimmy/go-common/http"
)

// 用户结构
type User struct {
    ID    int64  `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// 用户列表接口
func GetUserList(h *commonhttp.Handler) {
    // 获取分页参数
    pagination := h.ParsePaginationRequest()
    page := pagination.GetPage()
    pageSize := pagination.GetSize()
    
    // 获取查询参数
    keyword := h.GetQuery("keyword", "")
    
    // 查询数据
    users, total := queryUsers(keyword, page, pageSize)
    
    // 返回分页响应
    h.SuccessPage(users, total, page, pageSize)
}

// 创建用户接口
func CreateUser(h *commonhttp.Handler) {
    // 解析请求体
    var req struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }
    
    if err := h.ParseJSON(&req); err != nil {
        h.WriteJSON(http.StatusBadRequest, 400, "请求参数解析失败", nil)
        return
    }
    
    // 参数验证
    if req.Name == "" {
        h.Error(1001, "用户名不能为空")
        return
    }
    
    // 创建用户
    user, err := createUser(req.Name, req.Email)
    if err != nil {
        h.SystemError("创建用户失败")
        return
    }
    
    // 返回成功响应
    h.SuccessWithMessage("创建成功", user)
}

// 获取用户详情接口
func GetUser(h *commonhttp.Handler) {
    // 获取查询参数
    id := h.GetQueryInt64("id", 0)
    
    if id == 0 {
        h.WriteJSON(http.StatusBadRequest, 400, "用户ID不能为空", nil)
        return
    }
    
    // 查询用户
    user, err := getUserByID(id)
    if err != nil {
        h.SystemError("查询用户失败")
        return
    }
    
    if user == nil {
        h.Error(1002, "用户不存在")
        return
    }
    
    h.Success(user)
}

func main() {
    // 使用HandleFunc包装器推荐
    http.HandleFunc("/users", commonhttp.HandleFunc(func(h *commonhttp.Handler) {
        switch h.Request().Method {
        case http.MethodGet:
            GetUserList(h)
        case http.MethodPost:
            CreateUser(h)
        default:
            h.WriteJSON(http.StatusMethodNotAllowed, 405, "方法不支持", nil)
        }
    }))
    
    http.HandleFunc("/user", commonhttp.HandleFunc(GetUser))
    
    log.Println("Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

API 参考

Handler结构

Handler封装了ResponseWriterRequest提供更简洁的API。

type Handler struct {
    w http.ResponseWriter
    r *http.Request
}

创建Handler

NewHandler(w http.ResponseWriter, r *http.Request) *Handler

创建Handler实例。

HandleFunc(fn func(*Handler)) http.HandlerFunc

将Handler函数转换为标准的http.HandlerFunc便捷包装器

示例:

http.HandleFunc("/users", commonhttp.HandleFunc(func(h *commonhttp.Handler) {
    h.Success(data)
}))

Handler响应方法

(h *Handler) Success(data interface{})

成功响应HTTP 200业务code 0。

(h *Handler) SuccessWithMessage(message string, data interface{})

带消息的成功响应。

(h *Handler) Error(code int, message string)

业务错误响应HTTP 200业务code非0。

(h *Handler) SystemError(message string)

系统错误响应HTTP 500业务code 500。

(h *Handler) WriteJSON(httpCode, code int, message string, data interface{})

写入JSON响应自定义

参数:

  • httpCode: HTTP状态码
  • code: 业务状态码
  • message: 响应消息
  • data: 响应数据

(h *Handler) SuccessPage(list interface{}, total int64, page, pageSize int, message ...string)

分页成功响应。

参数:

  • list: 数据列表
  • total: 总记录数
  • page: 当前页码
  • pageSize: 每页大小
  • message: 响应消息(可选,如果为空则使用默认消息 "success"

Handler请求解析方法

(h *Handler) ParseJSON(v interface{}) error

解析JSON请求体。

(h *Handler) GetQuery(key, defaultValue string) string

获取查询参数(字符串)。

(h *Handler) GetQueryInt(key string, defaultValue int) int

获取查询参数(整数)。

(h *Handler) GetQueryInt64(key string, defaultValue int64) int64

获取查询参数int64

(h *Handler) GetQueryBool(key string, defaultValue bool) bool

获取查询参数(布尔值)。

(h *Handler) GetQueryFloat64(key string, defaultValue float64) float64

获取查询参数(浮点数)。

(h *Handler) GetFormValue(key, defaultValue string) string

获取表单值(字符串)。

(h *Handler) GetFormInt(key string, defaultValue int) int

获取表单值(整数)。

(h *Handler) GetFormInt64(key string, defaultValue int64) int64

获取表单值int64

(h *Handler) GetFormBool(key string, defaultValue bool) bool

获取表单值(布尔值)。

(h *Handler) GetHeader(key, defaultValue string) string

获取请求头。

(h *Handler) ParsePaginationRequest() *PaginationRequest

从请求中解析分页参数。

说明:

  • 支持从查询参数和form表单中解析
  • 优先级:查询参数 > form表单
  • 如果请求体是JSON格式且包含分页字段建议先使用ParseJSON解析完整请求体到包含PaginationRequest的结构体中

(h *Handler) GetTimezone() string

从请求的context中获取时区。

说明:

  • 如果使用了middleware.Timezone中间件可以从context中获取时区信息
  • 如果未设置,返回默认时区 AsiaShanghai

Handler访问原始对象

(h *Handler) ResponseWriter() http.ResponseWriter

获取原始的ResponseWriter需要时使用

(h *Handler) Request() *http.Request

获取原始的Request需要时使用

(h *Handler) Context() context.Context

获取请求的Context。

分页请求结构

PaginationRequest

分页请求结构支持从JSON和form中解析分页参数。

字段:

  • Page: 页码默认1
  • Size: 每页数量(兼容旧版本)
  • PageSize: 每页数量推荐使用优先于Size

方法:

  • GetPage() int: 获取页码如果未设置则返回默认值1
  • GetSize() int: 获取每页数量优先使用PageSize如果未设置则使用Size默认20最大100
  • GetOffset() int: 计算数据库查询的偏移量

ParsePaginationRequest(r *http.Request) *PaginationRequest

从请求中解析分页参数内部函数Handler内部使用

状态码说明

HTTP状态码

  • 200: 正常响应(包括业务错误)
  • 400: 请求参数错误
  • 401: 未授权
  • 403: 禁止访问
  • 404: 资源不存在
  • 500: 系统内部错误

业务状态码

  • 0: 成功
  • 非0: 业务错误(具体错误码由业务定义)

注意事项

  1. HTTP状态码与业务状态码分离

    • 业务错误如用户不存在、参数验证失败等返回HTTP 200业务code非0
    • 只有系统异常如数据库连接失败、程序panic等才返回HTTP 500
  2. 分页参数限制

    • page最小值为1
    • pageSize最小值为1最大值为100
  3. 响应格式统一

    • 所有响应都遵循标准结构
    • timestamp为Unix时间戳
  4. 错误处理

    • 使用Error方法返回业务错误HTTP 200业务code非0
    • 使用SystemError返回系统错误HTTP 500
    • 其他HTTP错误状态码400, 401, 403, 404等使用WriteJSON方法直接指定
  5. 黑盒模式

    • 所有功能都通过Handler对象调用无需传递wr参数
    • 代码更简洁,减少调用方工作量

示例

完整示例请参考 examples/http_handler_example.go