Refit接口定义必须满足:方法返回Task<T>或IObservable<T>;每个方法需标注HTTP动词特性(如[Get]);路径参数名须与方法参数一致或用[AliasAs]显式映射;接收原始响应需用Task<Response<T>>而非Task<HttpResponseMessage>。

Refit 不是“配好就能用”的 HTTP 客户端,它必须配合接口定义 + 正确注册的 HttpClient 才能工作;漏掉任一环节,立刻报 Type is not a Refit interface 或 NullReferenceException。
Refit 接口定义必须满足哪些硬性条件
Refit 在运行时靠反射扫描接口,一旦不满足基本契约,代理类根本生成不了。
- 方法必须返回
Task<T>或IObservable<T>—— 写成string GetUser(int id)编译通过,但运行时报错 - 每个方法必须带 HTTP 动词特性,如
[Get("/users/{id}")]、[Post("/login")];只写方法签名不加特性,请求 URL 会变成空或错误绝对路径 - 路径参数需与参数名一致,或用
[AliasAs("user_id")]显式映射(仅影响 URL 和 query,不影响 JSON 字段) - 如果要接收原始响应(比如检查状态码),得用
Task<Response<User>>,而不是Task<HttpResponseMessage>—— 后者 Refit 不识别
注册 Refit 客户端时为什么不能只传 BaseAddress
直接 services.AddRefitClient<IUserApi>().ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")) 看似够了,但实际埋了三个坑:
-
HttpClient缺少超时设置:默认Timeout = TimeSpan.FromSeconds(100),可能卡住整个请求链 - 没配置认证头:bearer token 不能写在每个接口方法里,要用
AddHttpMessageHandler<AuthHandler>()统一注入 - 没启用连接复用:手动 new
HttpClient容易触发 DNS 缓存失效或 socket 耗尽,必须走IHttpClientFactory
正确做法是在 ConfigureHttpClient 里补全关键配置:
services.AddRefitClient<IUserApi>()
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri("https://api.example.com");
c.Timeout = TimeSpan.FromSeconds(15);
})
.AddHttpMessageHandler<AuthHandler>();
JSON 反序列化失败?大概率是字段名没对齐
服务端返回 {"user_name": "leo"},但你的 User 类写的是 public string Name { get; set; },结果 Name 始终为 null —— 这不是 Refit 的 bug,是序列化器默认按 PascalCase 匹配。
-
[AliasAs("user_name")]没用:它只改 URL 参数名,不改 JSON 字段映射 - 必须用
[JsonPropertyName("user_name")](System.Text.Json.Serialization)或[JsonProperty("user_name")](Newtonsoft) - 如果项目用了
Newtonsoft.Json,得额外安装Refit.Newtonsoft.Json并传入自定义RefitSettings
为什么 RestService.For<T>(uri) 适合测试但不该用于生产
手动创建客户端看似简单:var api = RestService.For<IUserApi>("https://api.example.com"),但它绕过了 DI 容器和 IHttpClientFactory。
- 每次调用都 new 一个
HttpClient→ 连接泄漏、DNS 缓存僵化、端口耗尽 - 无法集中管理重试、熔断(比如用 Polly)、日志、指标等横切逻辑
- 测试时可以这么干(Mock 方便),但上线后必须走
AddRefitClient<T>()注册方式
真正容易被忽略的点在于:Refit 的“声明式”只是表象,底层一切行为(超时、重试、序列化、header 注入)都依赖于你传给它的 HttpClient 实例 —— 它不帮你做决策,只严格执行契约。接口写错一行,或 HttpClient 少配一个 BaseAddress,就直接崩给你看。


















