touka/docs/httpc.md
wjqserver 4f262b2497 docs: 添加 httpc 集成文档和示例
- 新增 examples/httpc 示例代码
- 新增 docs/httpc.md 文档说明
2026-04-22 07:13:55 +08:00

188 lines
5 KiB
Markdown
Raw Permalink 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 Client (httpc)
Touka 内置了 [httpc](https://github.com/WJQSERVER-STUDIO/httpc) HTTP 客户端,方便在请求处理函数中发起出站 HTTP 请求。
## 核心特性
- **自动 Context 关联**:使用 `HTTPC()` 方法时,出站请求会自动关联当前请求的 Context
- **请求取消传播**:当客户端断开连接时,出站请求会自动取消,避免资源泄漏
- **链式调用**:保持 httpc 原有的组合式构建器风格
## 基本用法
### 简单 GET 请求
```go
r.GET("/proxy", func(c *touka.Context) {
body, err := c.HTTPC().
GET("https://api.example.com/data").
Text()
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
c.String(200, body)
})
```
### POST JSON 请求
```go
r.POST("/users", func(c *touka.Context) {
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
c.ShouldBindJSON(&req)
var result struct {
ID int `json:"id"`
Name string `json:"name"`
}
err := c.HTTPC().
POST("https://api.example.com/users").
SetHeader("Authorization", "Bearer "+token).
SetJSONBody(req).
DecodeJSON(&result)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
c.JSON(200, result)
})
```
### 带查询参数
```go
r.GET("/search", func(c *touka.Context) {
query := c.Query("q")
var result SearchResult
err := c.HTTPC().
GET("https://api.example.com/search").
SetQueryParam("q", query).
SetQueryParam("limit", "10").
DecodeJSON(&result)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
c.JSON(200, result)
})
```
## API 对比
### 旧方式Deprecated
```go
// 需要手动 WithContext容易忘记
resp, err := c.Client().
WithContext(c.Context()).
GET(url).
Execute()
```
### 新方式(推荐)
```go
// 自动关联请求 Context
resp, err := c.HTTPC().
GET(url).
Execute()
```
## Context 取消机制
使用 `HTTPC()` 时,当客户端断开连接(如关闭浏览器),出站请求会自动取消:
```go
r.GET("/long-task", func(c *touka.Context) {
// 这个请求会在客户端断开时自动取消
resp, err := c.HTTPC().
GET("https://slow-api.example.com/data").
Execute()
// 如果客户端已断开err 会包含 context.Canceled
if errors.Is(err, context.Canceled) {
return // 客户端已断开,无需处理
}
// ...
})
```
## 完整 API
### contextHTTPClient 方法
| 方法 | 返回类型 | 说明 |
|------|----------|------|
| `NewRequestBuilder(method, url)` | `*httpc.RequestBuilder` | 创建通用请求构建器 |
| `GET(url)` | `*httpc.RequestBuilder` | 创建 GET 请求 |
| `POST(url)` | `*httpc.RequestBuilder` | 创建 POST 请求 |
| `PUT(url)` | `*httpc.RequestBuilder` | 创建 PUT 请求 |
| `DELETE(url)` | `*httpc.RequestBuilder` | 创建 DELETE 请求 |
| `PATCH(url)` | `*httpc.RequestBuilder` | 创建 PATCH 请求 |
| `HEAD(url)` | `*httpc.RequestBuilder` | 创建 HEAD 请求 |
| `OPTIONS(url)` | `*httpc.RequestBuilder` | 创建 OPTIONS 请求 |
### httpc.RequestBuilder 链式方法
返回 `*httpc.RequestBuilder`(用于链式调用):
| 方法 | 说明 |
|------|------|
| `WithContext(ctx)` | 设置 Context通常不需要已自动关联 |
| `NoDefaultHeaders()` | 不添加默认 Header |
| `SetHeader(key, value)` | 设置 Header |
| `AddHeader(key, value)` | 添加 Header可重复 |
| `SetHeaders(map)` | 批量设置 Headers |
| `SetQueryParam(key, value)` | 设置查询参数 |
| `AddQueryParam(key, value)` | 添加查询参数(可重复) |
| `SetQueryParams(map)` | 批量设置查询参数 |
| `SetBody(io.Reader)` | 设置请求 Body |
| `SetRawBody([]byte)` | 设置字节 Body |
返回 `(*httpc.RequestBuilder, error)`(可能失败):
| 方法 | 说明 |
|------|------|
| `SetJSONBody(any)` | 设置 JSON Body |
| `SetXMLBody(any)` | 设置 XML Body |
| `SetGOBBody(any)` | 设置 GOB Body |
### 终结方法
| 方法 | 返回类型 | 说明 |
|------|----------|------|
| `Build()` | `(*http.Request, error)` | 构建请求但不执行 |
| `Execute()` | `(*http.Response, error)` | 执行并返回原始响应 |
| `DecodeJSON(v)` | `error` | 执行并解码 JSON |
| `DecodeXML(v)` | `error` | 执行并解码 XML |
| `DecodeGOB(v)` | `error` | 执行并解码 GOB |
| `Text()` | `(string, error)` | 执行并返回文本 |
| `Bytes()` | `([]byte, error)` | 执行并返回字节 |
| `SSE()` | `(*SSEStream, error)` | 建立 SSE 流连接 |
## 迁移指南
### go:fix inline 兼容
旧代码 `c.GetHTTPC()` 可通过 `go fix` 自动迁移到 `c.Client()`
```bash
go fix ./...
```
### 手动迁移
| 旧代码 | 新代码 |
|--------|--------|
| `c.GetHTTPC()` | `c.Client()``c.HTTPC()` |
| `c.Client().WithContext(ctx).GET(url)` | `c.HTTPC().GET(url)` |
## 示例
完整示例请参考 [examples/httpc](../examples/httpc)。