20 KiB
HTTP Restful工具文档
概述
HTTP Restful工具提供了标准化的HTTP请求和响应处理功能,提供公共方法供外部调用,保持低耦合。
功能特性
- 低耦合设计:提供公共方法,不封装Handler结构
- 标准化的响应结构:
{code, message, timestamp, data} - 分离HTTP状态码和业务状态码
- 支持分页响应
- 提供便捷的请求参数解析方法
- 支持JSON请求体解析
- Factory黑盒模式:推荐使用
factory.Success()等方法
响应结构
标准响应结构
{
"code": 0,
"message": "success",
"timestamp": 1704067200,
"data": {}
}
结构体类型(暴露在 factory 中):
// 在 factory 包中可以直接使用
type Response struct {
Code int `json:"code"` // 业务状态码,0表示成功
Message string `json:"message"` // 响应消息
Timestamp int64 `json:"timestamp"` // 时间戳
Data interface{} `json:"data"` // 响应数据
}
分页响应结构
{
"code": 0,
"message": "success",
"timestamp": 1704067200,
"data": {
"list": [],
"total": 100,
"page": 1,
"pageSize": 10
}
}
结构体类型(暴露在 factory 中):
// 在 factory 包中可以直接使用
type PageData struct {
List interface{} `json:"list"` // 数据列表
Total int64 `json:"total"` // 总记录数
Page int `json:"page"` // 当前页码
PageSize int `json:"pageSize"` // 每页大小
}
type PageResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Timestamp int64 `json:"timestamp"`
Data *PageData `json:"data"`
}
使用暴露的结构体
外部项目可以直接使用 factory.Response、factory.PageData 等类型:
import "git.toowon.com/jimmy/go-common/factory"
// 创建标准响应对象
response := factory.Response{
Code: 0,
Message: "success",
Data: userData,
}
// 创建分页数据对象
pageData := &factory.PageData{
List: users,
Total: 100,
Page: 1,
PageSize: 20,
}
// 传递给 Success 方法
fac.Success(w, pageData)
使用方法
方式一:使用Factory黑盒方法(推荐)⭐
这是最简单的方式,直接使用 factory.Success() 等方法:
import (
"net/http"
"git.toowon.com/jimmy/go-common/factory"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func GetUser(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 获取查询参数(使用类型转换方法)
id := tools.ConvertInt64(r.URL.Query().Get("id"), 0)
// 返回成功响应(使用factory方法)
fac.Success(w, data)
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 解析JSON(使用公共方法)
var req struct {
Name string `json:"name"`
}
if err := commonhttp.ParseJSON(r, &req); err != nil {
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
// 返回成功响应(使用factory方法)
fac.Success(w, data, "创建成功")
}
func GetUserList(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 获取分页参数(使用factory方法,推荐)
pagination := fac.ParsePaginationRequest(r)
page := pagination.GetPage()
pageSize := pagination.GetPageSize()
// 获取查询参数(直接使用HTTP原生方法)
keyword := r.URL.Query().Get("keyword")
// 查询数据
list, total := getDataList(keyword, page, pageSize)
// 返回分页响应(使用factory方法)
fac.SuccessPage(w, list, total, page, pageSize)
}
// 注册路由
http.HandleFunc("/user", GetUser)
http.HandleFunc("/users", GetUserList)
方式二:直接使用公共方法
如果不想使用Factory,可以直接使用 http 包的公共方法:
import (
"net/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func GetUser(w http.ResponseWriter, r *http.Request) {
// 获取查询参数
id := tools.ConvertInt64(r.URL.Query().Get("id"), 0)
// 返回成功响应
commonhttp.Success(w, data)
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
// 解析JSON
var req struct {
Name string `json:"name"`
}
if err := commonhttp.ParseJSON(r, &req); err != nil {
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
// 返回成功响应
commonhttp.Success(w, data, "创建成功")
}
成功响应
// 使用Factory(推荐)
fac.Success(w, data) // 只有数据,使用默认消息 "success"
fac.Success(w, data, "操作成功") // 数据+消息
// 或直接使用公共方法
commonhttp.Success(w, data) // 只有数据
commonhttp.Success(w, data, "操作成功") // 数据+消息
错误响应
// 使用Factory(推荐)
fac.Error(w, 1001, "用户不存在") // 业务错误(HTTP 200,业务code非0)
fac.SystemError(w, "服务器内部错误") // 系统错误(HTTP 500)
// 或直接使用公共方法
commonhttp.Error(w, 1001, "用户不存在")
commonhttp.SystemError(w, "服务器内部错误")
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数错误", nil) // 自定义HTTP状态码(仅公共方法)
分页响应
// 使用Factory(推荐)
fac.SuccessPage(w, list, total, page, pageSize)
fac.SuccessPage(w, list, total, page, pageSize, "查询成功")
// 或直接使用公共方法
commonhttp.SuccessPage(w, list, total, page, pageSize)
commonhttp.SuccessPage(w, list, total, page, pageSize, "查询成功")
解析请求
解析JSON请求体
// 使用公共方法
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := commonhttp.ParseJSON(r, &req); err != nil {
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
获取查询参数
import "git.toowon.com/jimmy/go-common/tools"
// 字符串直接获取
name := r.URL.Query().Get("name")
// 使用类型转换方法
id := tools.ConvertInt(r.URL.Query().Get("id"), 0)
userId := tools.ConvertInt64(r.URL.Query().Get("userId"), 0)
isActive := tools.ConvertBool(r.URL.Query().Get("isActive"), false)
price := tools.ConvertFloat64(r.URL.Query().Get("price"), 0.0)
获取表单参数
import "git.toowon.com/jimmy/go-common/tools"
// 字符串直接获取
name := r.FormValue("name")
// 使用类型转换方法
age := tools.ConvertInt(r.FormValue("age"), 0)
userId := tools.ConvertInt64(r.FormValue("userId"), 0)
isActive := tools.ConvertBool(r.FormValue("isActive"), false)
获取请求头
// 直接使用HTTP原生方法
token := r.Header.Get("Authorization")
contentType := r.Header.Get("Content-Type")
if contentType == "" {
contentType = "application/json" // 设置默认值
}
获取分页参数
方式1:使用 PaginationRequest 结构(推荐)
// 定义请求结构(包含分页字段)
type ListUserRequest struct {
Keyword string `json:"keyword"`
commonhttp.PaginationRequest // 嵌入分页请求结构
}
// 从JSON请求体解析(分页字段会自动解析)
var req ListUserRequest
if err := commonhttp.ParseJSON(r, &req); err != nil {
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
// 使用分页方法
page := req.GetPage() // 获取页码(默认1)
pageSize := req.GetPageSize() // 获取每页数量(默认20,最大100)
offset := req.GetOffset() // 计算偏移量
方式2:从查询参数/form解析分页
// 使用公共方法
pagination := commonhttp.ParsePaginationRequest(r)
page := pagination.GetPage()
pageSize := pagination.GetPageSize()
offset := pagination.GetOffset()
获取时区
// 使用公共方法
// 如果使用了middleware.Timezone中间件,可以从context中获取时区信息
// 如果未设置,返回默认时区 AsiaShanghai
timezone := commonhttp.GetTimezone(r)
完整示例
使用Factory(推荐)
package main
import (
"log"
"net/http"
"git.toowon.com/jimmy/go-common/factory"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
// 用户结构
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// 用户列表接口
func GetUserList(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 获取分页参数(使用公共方法)
pagination := commonhttp.ParsePaginationRequest(r)
page := pagination.GetPage()
pageSize := pagination.GetPageSize()
// 获取查询参数(直接使用HTTP原生方法)
keyword := r.URL.Query().Get("keyword")
// 查询数据
users, total := queryUsers(keyword, page, pageSize)
// 返回分页响应(使用factory方法)
fac.SuccessPage(w, users, total, page, pageSize)
}
// 创建用户接口
func CreateUser(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 解析请求体(使用公共方法)
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := commonhttp.ParseJSON(r, &req); err != nil {
fac.WriteJSON(w, http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
// 参数验证
if req.Name == "" {
fac.Error(w, 1001, "用户名不能为空")
return
}
// 创建用户
user, err := createUser(req.Name, req.Email)
if err != nil {
fac.SystemError(w, "创建用户失败")
return
}
// 返回成功响应(使用factory方法)
fac.Success(w, user, "创建成功")
}
// 获取用户详情接口
func GetUser(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 获取查询参数(使用类型转换方法)
id := tools.ConvertInt64(r.URL.Query().Get("id"), 0)
if id == 0 {
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "用户ID不能为空", nil)
return
}
// 查询用户
user, err := getUserByID(id)
if err != nil {
fac.SystemError(w, "查询用户失败")
return
}
if user == nil {
fac.Error(w, 1002, "用户不存在")
return
}
// 返回成功响应(使用factory方法)
fac.Success(w, user)
}
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
GetUserList(w, r)
case http.MethodPost:
CreateUser(w, r)
default:
commonhttp.WriteJSON(w, http.StatusMethodNotAllowed, 405, "方法不支持", nil)
}
})
http.HandleFunc("/user", GetUser)
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
API 参考
Factory HTTP响应结构体(暴露给外部项目使用)
Response
标准响应结构体,外部项目可以直接使用 factory.Response。
字段:
Code: 业务状态码,0表示成功Message: 响应消息Timestamp: 时间戳(Unix时间戳,秒)Data: 响应数据
示例:
response := factory.Response{
Code: 0,
Message: "success",
Data: userData,
}
PageData
分页数据结构体,外部项目可以直接使用 factory.PageData。
字段:
List: 数据列表Total: 总记录数Page: 当前页码PageSize: 每页大小
示例:
pageData := &factory.PageData{
List: users,
Total: 100,
Page: 1,
PageSize: 20,
}
fac.Success(w, pageData)
PageResponse
分页响应结构体,外部项目可以直接使用 factory.PageResponse。
字段:
Code: 业务状态码Message: 响应消息Timestamp: 时间戳Data: 分页数据(*PageData)
Factory HTTP响应方法(推荐使用)
(f *Factory) Success(w http.ResponseWriter, data interface{}, message ...string)
成功响应,HTTP 200,业务code 0。
参数:
data: 响应数据,可以为nilmessage: 响应消息(可选),如果为空则使用默认消息 "success"
示例:
fac.Success(w, data) // 只有数据,使用默认消息 "success"
fac.Success(w, data, "操作成功") // 数据+消息
(f *Factory) Error(w http.ResponseWriter, code int, message string)
业务错误响应,HTTP 200,业务code非0。
示例:
fac.Error(w, 1001, "用户不存在")
(f *Factory) SystemError(w http.ResponseWriter, message string)
系统错误响应,HTTP 500,业务code 500。
示例:
fac.SystemError(w, "服务器内部错误")
(f *Factory) WriteJSON(w http.ResponseWriter, httpCode, code int, message string, data interface{})
写入JSON响应(自定义)。
参数:
httpCode: HTTP状态码code: 业务状态码message: 响应消息data: 响应数据
说明:
- 此方法不在 Factory 中,直接使用
commonhttp.WriteJSON() - 用于需要自定义HTTP状态码的场景(如 400, 401, 403, 404 等)
示例:
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数错误", nil)
commonhttp.WriteJSON(w, http.StatusUnauthorized, 401, "未登录", nil)
(f *Factory) SuccessPage(w http.ResponseWriter, list interface{}, total int64, page, pageSize int, message ...string)
分页成功响应。
参数:
list: 数据列表total: 总记录数page: 当前页码pageSize: 每页大小message: 响应消息(可选,如果为空则使用默认消息 "success")
示例:
fac.SuccessPage(w, users, total, page, pageSize)
fac.SuccessPage(w, users, total, page, pageSize, "查询成功")
HTTP公共方法(直接使用)
WriteJSON(w http.ResponseWriter, httpCode, code int, message string, data interface{})
写入JSON响应(自定义HTTP状态码和业务状态码)。
说明:
- 此方法不在 Factory 中,直接使用
commonhttp.WriteJSON() - 用于需要自定义HTTP状态码的场景(如 400, 401, 403, 404 等)
示例:
commonhttp.WriteJSON(w, http.StatusBadRequest, 400, "请求参数错误", nil)
commonhttp.WriteJSON(w, http.StatusUnauthorized, 401, "未登录", nil)
ParseJSON(r *http.Request, v interface{}) error
解析JSON请求体。
示例:
var req CreateUserRequest
if err := commonhttp.ParseJSON(r, &req); err != nil {
// 处理错误
}
获取查询参数和表单参数
推荐方式:使用类型转换工具
import "git.toowon.com/jimmy/go-common/tools"
// 字符串直接使用HTTP原生方法
name := r.URL.Query().Get("name")
if name == "" {
name = "default" // 设置默认值
}
// 类型转换使用tools包
id := tools.ConvertInt(r.URL.Query().Get("id"), 0)
userId := tools.ConvertInt64(r.URL.Query().Get("userId"), 0)
isActive := tools.ConvertBool(r.URL.Query().Get("isActive"), false)
price := tools.ConvertFloat64(r.URL.Query().Get("price"), 0.0)
// 表单参数类似
age := tools.ConvertInt(r.FormValue("age"), 0)
类型转换方法说明:
tools.ConvertInt(value string, defaultValue int) int- 转换为inttools.ConvertInt64(value string, defaultValue int64) int64- 转换为int64tools.ConvertUint64(value string, defaultValue uint64) uint64- 转换为uint64tools.ConvertUint32(value string, defaultValue uint32) uint32- 转换为uint32tools.ConvertBool(value string, defaultValue bool) bool- 转换为booltools.ConvertFloat64(value string, defaultValue float64) float64- 转换为float64
获取请求头:
// 直接使用HTTP原生方法
token := r.Header.Get("Authorization")
contentType := r.Header.Get("Content-Type")
ParsePaginationRequest(r *http.Request) *PaginationRequest
从请求中解析分页参数。
说明:
- 支持从查询参数和form表单中解析
- 优先级:查询参数 > form表单
- 如果请求体是JSON格式且包含分页字段,建议先使用
ParseJSON解析完整请求体到包含PaginationRequest的结构体中
GetTimezone(r *http.Request) string
从请求的context中获取时区。
说明:
- 如果使用了middleware.Timezone中间件,可以从context中获取时区信息
- 如果未设置,返回默认时区 AsiaShanghai
Success(w http.ResponseWriter, data interface{}, message ...string)
成功响应(公共方法)。
参数:
data: 响应数据,可以为nilmessage: 响应消息(可选),如果为空则使用默认消息 "success"
示例:
commonhttp.Success(w, data) // 只有数据
commonhttp.Success(w, data, "操作成功") // 数据+消息
Error(w http.ResponseWriter, code int, message string)
错误响应(公共方法)。
SystemError(w http.ResponseWriter, message string)
系统错误响应(公共方法)。
WriteJSON(w http.ResponseWriter, httpCode, code int, message string, data interface{})
写入JSON响应(公共方法,不在Factory中)。
说明:
- 用于需要自定义HTTP状态码的场景
- 直接使用
commonhttp.WriteJSON(),不在 Factory 中
SuccessPage(w http.ResponseWriter, list interface{}, total int64, page, pageSize int, message ...string)
分页成功响应(公共方法)。
分页请求结构
PaginationRequest
分页请求结构,支持从JSON和form中解析分页参数。
字段:
Page: 页码(默认1)PageSize: 每页数量
方法:
GetPage() int: 获取页码,如果未设置则返回默认值1GetPageSize() int: 获取每页数量,如果未设置则返回默认值20,最大限制100GetOffset() int: 计算数据库查询的偏移量
ParsePaginationRequest(r *http.Request) *PaginationRequest
从请求中解析分页参数(内部函数,Handler内部使用)。
状态码说明
HTTP状态码
200: 正常响应(包括业务错误)400: 请求参数错误401: 未授权403: 禁止访问404: 资源不存在500: 系统内部错误
业务状态码
0: 成功非0: 业务错误(具体错误码由业务定义)
注意事项
-
HTTP状态码与业务状态码分离:
- 业务错误(如用户不存在、参数验证失败等)返回HTTP 200,业务code非0
- 只有系统异常(如数据库连接失败、程序panic等)才返回HTTP 500
-
分页参数限制:
- page最小值为1
- pageSize最小值为1,最大值为100
-
响应格式统一:
- 所有响应都遵循标准结构
- timestamp为Unix时间戳(秒)
-
错误处理:
- 使用
Error方法返回业务错误(HTTP 200,业务code非0) - 使用
SystemError返回系统错误(HTTP 500) - 其他HTTP错误状态码(400, 401, 403, 404等)使用
WriteJSON方法直接指定
- 使用
-
推荐使用Factory:
- 使用
factory.Success()等方法,代码更简洁 - 直接使用
http包的公共方法,保持低耦合 - 不需要Handler结构,减少不必要的封装
- 使用
示例
完整示例请参考:
examples/http_handler_example.go- 使用Factory和公共方法examples/http_pagination_example.go- 分页示例examples/factory_blackbox_example.go- Factory黑盒模式示例