Files
go-common/docs/http.md

585 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# HTTP Restful工具文档
## 概述
HTTP Restful工具提供了标准化的HTTP请求和响应处理功能采用Handler黑盒模式封装了`ResponseWriter``Request`提供简洁的API无需每次都传递这两个参数。
## 功能特性
- **黑盒模式**:封装`ResponseWriter``Request`提供简洁的API
- **标准化的响应结构**`{code, message, timestamp, data}`
- **分离HTTP状态码和业务状态码**
- **支持分页响应**
- **提供便捷的请求参数解析方法**
- **支持JSON请求体解析**
## 响应结构
### 标准响应结构
```json
{
"code": 0,
"message": "success",
"timestamp": 1704067200,
"data": {}
}
```
### 分页响应结构
```json
{
"code": 0,
"message": "success",
"timestamp": 1704067200,
"data": {
"list": [],
"total": 100,
"page": 1,
"pageSize": 10
}
}
```
## 使用方法
### 1. 创建Handler
```go
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. 成功响应
```go
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. 错误响应
```go
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. 分页响应
```go
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请求体
```go
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...
}
```
#### 获取查询参数
```go
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)
}
```
#### 获取表单参数
```go
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)
}
```
#### 获取请求头
```go
func handler(h *commonhttp.Handler) {
token := h.GetHeader("Authorization", "")
contentType := h.GetHeader("Content-Type", "application/json")
}
```
#### 获取分页参数
**方式1使用 PaginationRequest 结构(推荐)**
```go
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()
}
```
#### 获取时区
```go
func handler(h *commonhttp.Handler) {
// 从请求的context中获取时区
// 如果使用了middleware.Timezone中间件可以从context中获取时区信息
// 如果未设置,返回默认时区 AsiaShanghai
timezone := h.GetTimezone()
}
```
### 6. 访问原始对象
如果需要访问原始的`ResponseWriter``Request`
```go
func handler(h *commonhttp.Handler) {
// 获取原始ResponseWriter
w := h.ResponseWriter()
// 获取原始Request
r := h.Request()
// 获取Context
ctx := h.Context()
}
```
## 完整示例
```go
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封装了`ResponseWriter``Request`提供更简洁的API。
```go
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便捷包装器
**示例:**
```go
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对象调用无需传递`w``r`参数
- 代码更简洁,减少调用方工作量
## 示例
完整示例请参考 `examples/http_handler_example.go`