
后端代码生成API设计【免费下载链接】refitThe automatic type-safe REST library for .NET. Refit turns a REST API into a C# interface and generates the HttpClient implementation, with support for HttpClientFactory, pluggable serializers and a testing package.项目地址https://gitcode.com/gh_mirrors/re/refit点击查看免费下载导读Refit 不仅是一个把 REST API 变成 C# 接口的运行时库还内置了一整套基于 Roslyn 的编译期工具链源生成器Source Generator、诊断分析器Analyzer与代码修复器Code Fix Provider。本文以仓库中的 Tooling 示例工程 为主体讲解如何像调用普通库一样在 .NET 10 C# 14 主机中直接驱动InterfaceStubGeneratorV2与RefitInterfaceAnalyzer验证生成代码、RF003 路由诊断、RF003/RF005 代码修复、标识符辅助类型以及公共Index/Rangepolyfill并读懂 Refit 保留 API 的兼容性诊断CS0618/CS0619/RF006。读完本文你将掌握 Refit 编译器组件的运行方式、项目引用组织技巧与一套可直接复用的编译期验证脚手架。一、示例工程概览编译器工具链的“体检”沙箱仓库中的src/examples/Documentation/Tooling/是一个专门用于演示 Refit 编译器组件的可执行工程。它不做任何 HTTP 请求而是直接运行编译器工具本身因此被 README 明确称为 “Compiler tooling samples”。工程核心信息来自 Tooling.csproj目标框架net10.0语言版本LangVersion14.0C# 14引用 Roslyn 5.0.0 的Microsoft.CodeAnalysis.CSharp与Microsoft.CodeAnalysis.CSharp.Workspaces通过VersionOverride覆盖通过ProjectReference直接引用 Refit 源码工程Refit.csproj、Refit.Xml.csproj、InterfaceStubGenerator.Roslyn48.csproj、Refit.Analyzers.Roslyn48.csproj、Refit.CodeFixes.Roslyn48.csprojIsAotCompatible为false该主机依赖普通 .NET 运行时与编译器元数据文件并非 Native AOT 示例。这里需要理解一个版本分层设计Refit 对外发布的编译器组件本身以 .NET Standard 2.0 为目标、针对 Roslyn 4.8 编译而示例主机使用更新的 Roslyn 5.0.0 来解析 C# 14 语法。主机的新版本编译器并不会改变那些发布组件恰好用来验证“老组件 新宿主”的兼容性。工程结构非常清晰每个.cs文件对应一类验证职责文件验证内容Program.cs依次驱动全部示例并输出Tooling samples passed.ToolingCompilation.cs构建带运行时与 Refit 元数据引用的 C# 14 编译单元GeneratorSample.cs用CSharpGeneratorDriver驱动源生成器AnalyzerSample.cs用Compilation.WithAnalyzers驱动分析器并检查 RF003CodeFixSample.cs注册代码动作并应用 RF003/RF005 修复CompatibilitySample.cs探测保留 API 的 CS0618/CS0619/RF006 兼容性诊断NameSample.cs验证UniqueNameBuilder与WellKnownTypesPolyfillSample.cs分别验证生成器/分析器各自携带的System.Index、System.RangeCheck.cs把失败的断言变成可执行失败InvalidOperationException二、构建与运行两条命令验证整套工具链README 给出了从仓库根目录执行的完整命令。注意工作目录是src/不是仓库根目录dotnet build examples/Documentation/Tooling/Tooling.csproj -c Release -p:LangVersion14.0 dotnet run --project examples/Documentation/Tooling/Tooling.csproj -c Release --no-build第一条命令以 Release 配置显式指定 C# 14 语言版本进行构建第二条使用--no-build直接运行避免重复编译。正常运行结束时Program.cs 会打印Tooling samples passed.。任何一步演示契约未满足都会由 Check.Require 抛出InvalidOperationException使进程失败——这是一种把“编译期工具的行为验证”转化为“可执行断言”的典型做法。三、宿主编译脚手架如何为演示准备编译器输入所有示例共享同一个编译构建工具类 ToolingCompilation.cs它做了三件事收集元数据引用从TRUSTED_PLATFORM_ASSEMBLIES读取运行时程序集路径排除宿主自身目录下的程序集再显式加入输出目录中的Refit.dll与Refit.Xml.dll组成编译器的MetadataReference集合固定解析选项CSharpParseOptions使用LanguageVersion.CSharp14保证演示代码以 C# 14 语法解析构建可编译单元Create(source)将源代码字符串解析为语法树并以OutputKind.DynamicallyLinkedLibrary、可空上下文启用NullableContextOptions.Enable创建名为ToolingDemonstration的库编译RequireNoErrors则收集所有DiagnosticSeverity.Error一旦存在就抛出携带实际编译器错误的异常。这个脚手架的意义在于它把“粘贴一段接口代码到 IDE 里看诊断”变成“在任意 .NET 进程里程序化地编译并断言结果”是编写编译期测试或文档示例的通用模板。四、驱动源生成器GeneratorSample 与 RF006 生成后编译验证GeneratorSample.cs 演示了如何驱动 Refit 源生成器。核心链路如下extern alias GeneratorTooling; using InterfaceStubGeneratorV2 GeneratorTooling::Refit.Generator.InterfaceStubGeneratorV2; CSharpCompilation compilation ToolingCompilation.Create(source); InterfaceStubGeneratorV2 generator new(); GeneratorDriver driver CSharpGeneratorDriver.Create( [generator.AsSourceGenerator()], parseOptions: new(LanguageVersion.CSharp14)); driver driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation generated, out _); GeneratorDriverRunResult result driver.GetRunResult();关键点演示输入是一个极简的 Refit 接口[Refit.Get(/people/{id})] Taskstring GetAsync(int id)generator.AsSourceGenerator()将InterfaceStubGeneratorV2包装为可被驱动执行的ISourceGenerator对应 README 所说的 “A generator driver callsInterfaceStubGeneratorV2.Initialize”驱动执行后代码断言result.GeneratedTrees非空生成器确实产出了客户端代码且generated编译无错误更严格的身份校验通过generated.GetTypeByMetadataName同时找到原始契约IPeople与生成实现Refit.Implementation.GeneratedToolingDemonstrationIPeople再用SymbolEqualityComparer.Default遍历生成类型的AllInterfaces确认生成客户端实现了编译输入中的真实接口。这印证了 Refit 生成代码的命名约定实现类位于Refit.Implementation命名空间下以 “GeneratedToolingDemonstration 接口名” 形式呈现本例演示主机下为GeneratedToolingDemonstrationIPeople。从源码结构看该命名由 InterfaceStubGeneratorV2.cs 与 Parser.GeneratedNaming.cs 共同决定。五、驱动分析器AnalyzerSample 与 RF003 路由诊断AnalyzerSample.cs 演示了分析器的运行与诊断清单校验。演示输入刻意使用了反斜杠路由[Refit.Get(\people)] System.Threading.Tasks.Taskstring GetAsync();运行方式同样是程序化驱动RefitInterfaceAnalyzer analyzer new(); ImmutableArrayDiagnostic diagnostics await compilation .WithAnalyzers([analyzer]) .GetAnalyzerDiagnosticsAsync();结果断言反斜杠路由必须产生RF003InvalidRouteBackslash即 “Refit route contains a backslash”。更重要的是一次“清单完整性”校验analyzer.SupportedDiagnostics必须恰好暴露 9 个诊断 ID即RF001Refit 成员缺少合法 HTTP 方法特性InvalidRefitMemberRF003路由含反斜杠InvalidRouteBackslashRF004方法声明了多个 CancellationTokenMultipleCancellationTokensRF005[HeaderCollection]参数类型不受支持InvalidHeaderCollectionParameterRF006方法回退到反射请求构建器、与纯生成注册不兼容GeneratedRequestBuildingFallbackRF008声明了多个[HeaderCollection]参数MultipleHeaderCollectionsRF009声明了多个[Authorize]参数MultipleAuthorizeParametersRF011声明了多个[Body]参数MultipleBodyParametersRF012multipart 方法同时声明了[Body]参数MultipartBodyParameter。这些 ID 与语义在 Refit.Analyzers.Shared/DiagnosticIds.cs 中一一对应示例同时断言所有诊断默认都是Warning且默认启用DefaultSeverity DiagnosticSeverity.Warning IsEnabledByDefault。这也是 README 中 “checks RF003 route analysis” 的落地细节。六、驱动代码修复CodeFixSample 中 RF003 与 RF005 的完整修复闭环CodeFixSample.cs 演示了比分析更进一步的“修复闭环”先制造错误代码再让RefitInterfaceCodeFixProvider产出修复动作最后验证修复后的编译既无错误也不再出现原诊断。工作流程FixAsync方法用AdhocWorkspace创建一个名为FixDemo的 C# 工程与文档Api.cs取该文档对应的Compilation调用AnalyzerSample.DiagnoseAsync收集诊断找到目标 IDRF003 或 RF005对应的Diagnostic实例化RefitInterfaceCodeFixProvider用CodeFixContext触发RegisterCodeFixesAsync收集CodeAction执行actions[0].GetOperationsAsync从操作中提取ApplyChangesOperation拿到修复后的Document重新编译修复后的文档必须无错误且重新跑分析器后目标诊断 ID 不再出现。示例覆盖了两个可修复 IDRF003 修复输入是AnalyzerSample.BrokenRoute反斜杠路由修复应把\people中的反斜杠规范化RF005 修复输入是[HeaderCollection] string headers不支持的类型修复应给出合法写法。同时断言provider.FixableDiagnosticIds恰好包含且仅包含RF003与RF005两个 ID且GetFixAllProvider()非空——即该修复器支持 “Fix All”。这正是 README “both RF003/RF005 corrections” 的含义对应的实现位于 Refit.CodeFixes.Shared/RefitInterfaceCodeFixProvider.cs。七、兼容性探测CompatibilitySample 中保留 API 的诊断契约Refit 在演进过程中保留了一批旧 API但通过[Obsolete]或[Obsolete(..., error: true)]显式标记。CompatibilitySample 把“探测这些诊断是否仍然存在”做成了 5 个独立断言对应 CompatibilitySample.cs探测目标预期诊断说明[AttachmentName(sent.bin)]直接用于 multipart 参数CS0618警告旧附件命名特性被保留但标记过时默认生成的客户端使用[FormObject]RF006multipart 表单对象展平会回退到反射构建器与纯生成注册不兼容直接new Refit.JsonContentSerializer()CS0619错误构造器级别过时直接构造即编译错误直接引用BodySerializationMethod.JsonCS0618警告旧 JSON 请求体枚举成员保留但过时读写Refit.XmlReaderWriterSettings.AllowDtdProcessingCS0618 × 2XML DTD 选择加入/退出同时保留警告需恰好出现 2 个注意 README 强调的表述CheckXmlDtdWarning对读写两处访问各产生一次 CS0618因此断言warnings accessorCount2 次而CheckJsonContentSerializer是 CS0619 错误级诊断RequireNoErrors之前先单独捕获它避免误判。这些都是“预期内的发现”——编译器探测工具专门用来暴露它们而工程本身在无抑制、无警告的条件下正常构建。八、标识符与类型辅助NameSample 中的 UniqueNameBuilder 与 WellKnownTypes生成器内部有两个重要的“名称与类型”辅助工具NameSample.cs 直接对它们做了契约级验证实现见 UniqueNameBuilder.cs 与 WellKnownTypes.cs。UniqueNameBuilder的行为验证先Reserve(client)再Reserve([client0, response])同时验证单个与枚举两种重载New(client)必须跳过已保留的名字得到client1已返回的名字会继续被保留因此下一次New(client)得到client2枚举形式的保留同样占用原名所以New(response)得到response0大小写敏感New(Client)原样返回Client。WellKnownTypes的查找行为验证Get(typeof(string))通过编译元数据名返回System.String符号TryGet(Demo.Missing)对不存在的类型返回null且重复查询保持null结果两种查询共享同一缓存符号用ReferenceEquals断言必得查询Get对编译引用中不存在的类型如NameSample自身抛出InvalidOperationException消息为Could not get type ...对没有元数据全名的运行时类型如List的类型参数同样拒绝消息为Could not get name of type ...。这些细节保证了生成器在生成命名与类型解析时行为可预期也是生成代码能稳定命名的底层支撑。九、多副本 PolyfillPolyfillSample 中 Index/Range 的隔离验证这是示例中最“精巧”的一部分。Refit 的生成器与分析器各自内置了公共System.Index、System.Range类型副本用于在 .NET Standard 2.0 目标下使用切片语法。Tooling 工程通过项目引用别名把它们隔离开ProjectReference Include../../../InterfaceStubGenerator.Roslyn48/InterfaceStubGenerator.Roslyn48.csproj AliasesGeneratorTooling / ProjectReference Include../../../Refit.Analyzers.Roslyn48/Refit.Analyzers.Roslyn48.csproj AliasesAnalyzerTooling /在 PolyfillSample.cs 中extern alias GeneratorTooling; extern alias AnalyzerTooling; using AnalyzerIndex AnalyzerTooling::System.Index; using AnalyzerRange AnalyzerTooling::System.Range; using GeneratorIndex GeneratorTooling::System.Index; using GeneratorRange GeneratorTooling::System.Range;extern alias让这两个公共类型副本既互相隔离又与 .NET 运行时的System.Index/System.Range隔离。普通应用程序直接使用运行时类型而这里验证的是编译器组件自身携带的那一份。对四个身份Generator 的 Index/Range 与 Analyzer 的 Index/Range逐一验证Index 契约构造参数位置值与fromEnd被保留GetOffset(length)从前端/后端计算偏移如new Index(1, fromEnd: true).GetOffset(4) 3Start/End描述序列边界GetOffset(4)分别为 0 与 4隐式转换Index←int相等性、GetHashCode、ToString都按存储字段描述Range 契约构造参数保留两端点All覆盖序列两端StartAt/EndAt工厂保留指定端点值相等性按两端点比较ToString包含 “Range” 字样负值与跨副本隔离两副本都保留负数构造值new Index(-1, fromEnd: true)的Value -1但生成器的Index与分析器的Index用Equals(object)互相比较时必然为falseGeneratorRange.All.Equals((object)AnalyzerRange.All)同样为false——类型是不同身份。这段验证直接对应 README 的说明extern alias保证两个组件各自携带的公共System.Index/System.Range副本互不干扰polyfill 示例解释了编译工具面compiled tooling surface的构成。十、设计要点回顾把文档示例转化为可复用实践综合 README 与源码这套示例沉淀出几条值得迁移的实践用源码工程引用而非 NuGet 包Tooling 工程通过ProjectReference直连生成器/分析器/修复器/Refit 本体便于在仓库内验证最新源码行为对外部使用者而言等价做法是引用 Refit 的 NuGet 包并读取其分析器/生成器程序集。新编译器宿主 旧发布组件宿主可用 Roslyn 5.0.0 解析 C# 14而 Refit 发布组件仍以 Roslyn 4.8 编译、.NET Standard 2.0 为目标二者解耦这正是编译期组件向后兼容的典型架构。程序化断言取代人工检查每个示例都用Check.Require把“行为契约”变成进程退出码级别的失败信号适合接入 CI 作为编译期回归测试。注意 AOT 边界该主机需要普通 .NET 运行时和编译器元数据文件不是 Native AOT 场景IsAotCompatiblefalse若要验证 AOT 兼容应参考仓库中的 Refit.NativeAotSmoke 工程。诊断与修复的“可证明闭环”分析器暴露 9 个默认警告级诊断修复器只对 RF003/RF005 提供修复并支持 Fix All兼容性 API 则通过 CS0618/CS0619/RF006 在编译期显式提示迁移路径详见 docs/breaking-changes.md。结语本文从 Tooling 示例 出发逐步拆解了 Refit 编译器工具链的完整驱动方式从构建运行命令、宿主编译脚手架到生成器驱动、分析器诊断清单RF001–RF012、代码修复闭环RF003/RF005、兼容性探测CS0618/CS0619/RF006、标识符与类型辅助以及双组件多副本Index/Range的隔离验证。无论你是想为 Refit 贡献编译期能力、还是希望在自己的项目里用类似手法验证 Roslyn 组件这套示例都提供了一份结构清晰、可直接运行的参考实现。赞分享后端代码生成API设计【免费下载链接】refitThe automatic type-safe REST library for .NET. Refit turns a REST API into a C# interface and generates the HttpClient implementation, with support for HttpClientFactory, pluggable serializers and a testing package.项目地址https://gitcode.com/gh_mirrors/re/refit点击查看免费下载相关推荐Humanizer.Analyzersv2 到 v3 迁移的 Roslyn 分析器与代码修复实战指南Humanizer.Analyzersv2 到 v3 迁移的 Roslyn 分析器与代码修复实战指南 Humanizer v3 将原本散落在 Humanize开发工具MessagePack-CSharp代码生成器原理Roslyn编译器集成技术MessagePack CSharp代码生成器原理Roslyn编译器集成技术 想要了解MessagePack CSharp代码生成器如何实现高效的序列化性能吗序列化后端Refit源生成器技术揭秘编译时代码生成的黑科技Refit源生成器技术揭秘编译时代码生成的黑科技 Refit 是一个为.NET生态系统设计的革命性REST客户端库它通过接口声明的方式自动生成HTTP AP后端代码生成API设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考