# 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`