共计 2899 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点分析
直接使用原生 HttpClient 调用 ChatGPT API 时,开发者常遇到以下问题:

- 令牌管理复杂:手动处理 OAuth2.0 令牌的获取与刷新,容易引入安全漏洞
- 流式响应解析困难 :ChatGPT 的 streaming 模式返回 SSE(Server-Sent Events) 格式数据,需要特殊处理
- 超时控制缺失 :长对话场景下未合理配置
CancellationToken可能导致线程阻塞 - 重试机制不足:API 限流或网络抖动时缺乏自动重试策略
HTTP 库技术选型
| 方案 | 优势 | 劣势 |
|---|---|---|
| 原生 HttpClient | 无需依赖第三方库 | 需要手动实现重试 / 熔断逻辑 |
| RestSharp | 简洁的链式调用语法 | 对 streaming 支持较弱 |
| Refit | 强类型接口定义 | 动态 Header 处理不够灵活 |
推荐使用 IHttpClientFactory 官方方案,兼具灵活性与可维护性。
核心实现步骤
1. 配置带策略的 HttpClient
// Program.cs
builder.Services.AddHttpClient("ChatGPT", client =>
{client.BaseAddress = new Uri("https://api.openai.com/v1/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
})
.AddPolicyHandler(GetRetryPolicy());
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() =>
HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
⚠️ 重要提示:
– 生产环境必须配置 BaseAddress 而非硬编码 URL
– 429 状态码需特殊处理
2. 流式响应解析
public async IAsyncEnumerable<string> StreamCompletionAsync(
ChatRequest request,
[EnumeratorCancellation] CancellationToken ct = default)
{
using var response = await _httpClient.PostAsJsonAsync(
"chat/completions",
request,
ct);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(ct);
using var reader = new StreamReader(stream);
while (!reader.EndOfStream && !ct.IsCancellationRequested)
{var line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line) || !line.StartsWith("data:"))
continue;
var eventData = line["data:".Length..].Trim();
if (eventData == "[DONE]") break;
var chunk = JsonSerializer.Deserialize<ChatChunk>(eventData);
yield return chunk.choices[0].delta.content;
}
}
3. OAuth2.0 认证实现
// 使用 Azure Key Vault 存储密钥
var credential = new DefaultAzureCredential();
var secret = await new SecretClient(new Uri(keyVaultUrl),
credential)
.GetSecretAsync("ChatGPT-Key");
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", secret.Value.Value);
生产环境建议
速率限制规避
- 实现请求队列(如通过
Channel类) - 监控
x-ratelimit-remaining响应头 - 重要业务请求添加优先级标识
上下文管理
public class ConversationContext
{public List<ChatMessage> History { get;} = new();
public void AddMessage(ChatRole role, string content)
=> History.Add(new ChatMessage(role, content));
public ChatRequest ToRequest() => new() {
messages = History,
max_tokens = 2000 // 动态计算剩余 token
};
}
性能对比数据
| 模式 | 内存占用 | 响应延迟 | 适用场景 |
|---|---|---|---|
| Streaming | 15-20MB | 1-3s | 实时对话 / 长文本 |
| 非 Streaming | 80-100MB | 5-8s | 短文本 / 批处理 |
扩展思考
结合 Blazor 实现实时 UI 的推荐方案:
- 在
OnInitializedAsync中启动 streaming 请求 - 通过
StateHasChanged()触发局部刷新 - 使用
CancellationTokenSource实现停止响应功能
@implements IAsyncDisposable
@inject ChatGPTService Service
<button @onclick="StartChat"> 开始对话 </button>
<button @onclick="cts?.Cancel()"> 停止 </button>
@foreach (var chunk in chunks)
{<p>@chunk</p>}
@code {private List<string> chunks = new();
private CancellationTokenSource? cts;
private async Task StartChat()
{cts = new CancellationTokenSource();
await foreach (var chunk in Service.StreamCompletionAsync(cts.Token))
{chunks.Add(chunk);
StateHasChanged();}
}
public async ValueTask DisposeAsync() => cts?.Cancel();
}
正文完
发表至: 未分类
近两天内
