Refit不是.NET原生组件,需显式安装NuGet包并配合特性标注的接口使用;所有客户端必须通过RestService.For()生成,不可直接new,且需启用源生成器、正确标注参数用途,否则编译或运行时报错。

Refit 不是 .NET 原生组件,它需要显式安装 NuGet 包且必须配合接口定义使用;直接 new Refit 会编译失败,这是新手最常卡住的第一步。
Refit 安装与基础接口定义
Refit 的核心是「接口 + 特性标注」,它不提供类实例化入口,所有客户端对象都由 RestService.For<t>()</t> 生成。没装包、没加特性、没启用源生成器,三者缺一都会报错。
- 必须安装
RefitNuGet 包(.NET 6+ 推荐同时装Refit.SourceGenerator,避免运行时反射开销) - 接口必须用
[Get]、[Post]等 Refit 特性标注方法,不能只写普通方法签名 - 接口方法参数需用
[Query]、[Body]、[Header]明确标注用途,否则默认全部当 URL 路径变量处理 - .NET 6+ 项目需在
.csproj中启用源生成:<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>
示例接口定义:
[Headers("User-Agent: MyApp/1.0")]
public interface IGitHubApi
{
[Get("/users/{login}")]
Task<User> GetUserAsync([AliasAs("login")] string userName);
<pre class="brush:php;toolbar:false;">[Post("/repos")]
Task<Repo> CreateRepoAsync([Body] RepoCreationRequest request);}
Refit 与 HttpClientFactory 集成
Refit 本身不管理连接生命周期,它只是把接口调用转成 HttpClient 请求。若不接入 IHttpClientFactory,每次 RestService.For<t>()</t> 都可能新建底层 HttpClient,引发端口耗尽或 DNS 缓存失效。
- 注册时用
AddRefitClient<t>()</t>(来自Refit.HttpClientFactory包),它会自动绑定IHttpClientFactory和重试策略 - 不要手动 new
HttpClient再传给RestService.For<t>(client)</t>,这绕过了工厂管理 - 超时、默认 Header、证书校验等配置,统一在
AddRefitClient的委托里设置,而非接口上硬编码
注册示例:
services.AddRefitClient<IGitHubApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.github.com/"))
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddHttpMessageHandler<AuthHandler>();常见编译错误和运行时陷阱
Refit 错误往往在编译期就暴露,但含义模糊。比如 CS0246: 未能找到类型或命名空间名 'Get' 并非缺少引用,而是没加 using Refit; —— Refit 特性全在该命名空间下。
-
CS8773: 接口成员 'X' 没有实现:接口方法用了[Body]但参数类型不是 class 或 record,Refit 不支持 struct 作为请求体 - 运行时报
System.InvalidOperationException: No parameterless constructor defined for type 'X':响应反序列化失败,检查 JSON 字段名是否匹配(默认区分大小写),或加[JsonPropertyName("id")] - GET 请求带复杂对象参数时,Refit 默认展开为 query string,但后端若期望 JSON body,必须改用
[Body]+POST,GET 无法发 body - 未启用源生成器时,.NET 6+ 项目可能静默跳过 Refit 生成,导致运行时报
NotSupportedException: Refit stubs not generated
Refit 不适合的场景
Refit 是声明式抽象,不是万能胶。当需要精细控制请求流(如分块上传、进度回调、自定义压缩逻辑)、或服务端接口严重不规范(路径含动态多层嵌套、header 与 query 强耦合)时,硬套 Refit 反而增加调试成本。
- 文件上传需进度监听 → 直接用
HttpClient.PostAsync()+MultipartFormDataContent - 需要复用同一个
HttpClient实例做长连接保活 → 绕过 Refit,自己管理 client 生命周期 - 接口返回格式混乱(有时 JSON,有时纯文本,有时带 HTML 错误页)→ 先用
HttpClient.SendAsync()拿到原始HttpResponseMessage再判断 - 微服务间调用且已用 OpenFeign → C# 侧没必要再引入 Refit,统一用
IHttpClientFactory+ 手动序列化更可控
Refit 的价值在「接口即契约」——但前提是契约本身足够清晰。一旦后端文档缺失或频繁变更,维护接口定义的成本可能超过收益。


















