首页 / 资讯中心 / 文章详情

ASP.NET Core WebApi服务端与Refit客户端契约开发实战

ASP.NET Core WebApi服务端与Refit客户端契约开发实战 ★ FEATURED ARTICLE
简介本资源是一套完整的WebApi服务开发与调用实践项目面向.NET初学者及中级开发者聚焦RESTful接口设计、后端服务构建与客户端HTTP通信核心能力训练。资源包含WebApi服务端项目与独立的访问客户端项目覆盖从API定义、控制器实现、路由配置到HttpWebRequest同步调用的全流程特别适合学习分布式系统中前后端解耦交互的典型模式。压缩包共553个文件主体为73个C#源码文件含控制器、业务逻辑与调用入口、119个运行依赖DLL、57个CSS与35个JS前端资源支撑配套页面交互以及sln解决方案、csproj工程文件和数据库文件mdf/ldf等整体15.96MB结构完整、开箱即用。已有1467人学习下载提供带详细注释的可运行源码、清晰分层的目录组织含AccessWebApi/WebApi/WebApi_BLL等模块及配套ASPX管理页面如CustomerList.aspx、Order.aspx助力读者快速理解WebApi生命周期与真实调用链路。1. WebApi和访问WebApi两个项目不是“写接口”和“调接口”那么简单而是服务契约落地的最小闭环你有没有遇到过这种场景后端同事说“API写好了文档在Swagger里”前端一接就404、500、字段对不上、时间戳格式报错或者自己写了个WebApi本地Postman能通部署到IIS就返回空白页这不是人的问题是“WebApi项目”和“访问WebApi项目”之间缺了一层可验证、可复现、可调试的服务契约实践。这套资源不是教你怎么用[HttpGet]或HttpClient而是把一个真实可用的WebApi服务含身份校验、日志埋点、异常统一处理和一个配套的强类型调用客户端自动生成DTO、支持重试、自动解析错误响应打包成两个独立但咬合紧密的VS解决方案——前者是服务端骨架后者是消费端样板。适合刚从MVC跳到前后端分离的.NET开发者、需要快速搭建内部微服务通信链路的中小团队以及正在被“接口联调地狱”折磨的全栈同学。它不依赖云平台、不绑定特定数据库跑起来只要.NET 6 SDK Visual Studio 2022或VS Code C# extension核心逻辑全部开源可读改一行就能看到效果。2. 项目结构与技术选型为什么是这两个项目而不是一个这两个项目不是随意拆分的它们各自承担明确职责并通过契约OpenAPI/Swagger和类型系统C# record / DTO实现松耦合。我拆开源码包第一眼就确认了这点服务端项目叫WebApi.Server客户端项目叫WebApi.Client命名直白但精准。它们共同构成一个“最小可行服务交互单元”——没有网关、没有注册中心、没有消息队列只有最朴素的HTTPJSON通信却覆盖了生产环境90%的基础需求路由、序列化、错误处理、认证、日志、客户端重试、类型安全调用。2.1 WebApi.Server不只是Controller而是一套可配置的服务骨架WebApi.Server是一个标准的 ASP.NET Core Web API 项目.NET 6但它不是空模板。打开.csproj文件你会看到关键依赖PackageReference IncludeSwashbuckle.AspNetCore Version6.5.0 / PackageReference IncludeMicrosoft.AspNetCore.Authentication.JwtBearer Version6.0.26 / PackageReference IncludeSerilog.AspNetCore Version6.1.1 / PackageReference IncludeMicrosoft.EntityFrameworkCore.InMemory Version6.0.26 /提示这里用的是In-Memory Database而非SQL Server目的很明确——让项目开箱即跑不依赖外部数据库安装。你完全可以在Program.cs里把AddInMemoryDatabase(DemoDb)替换成AddSqlServer(...)但首次运行请务必先用内存库验证流程。它的目录结构非常克制Controllers/ ValuesController.cs // 示例控制器带[Authorize] Models/ ValueDto.cs // 输出DTO使用record声明不可变、自动ToString/Equals Services/ IValueService.cs // 业务接口抽象 ValueService.cs // 内存实现含模拟延迟和异常 Program.cs // 主入口含Serilog日志配置、JWT Bearer认证、Swagger启用重点看Program.cs中的认证配置builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options { options.TokenValidationParameters new TokenValidationParameters { ValidateIssuer false, // 开发阶段简化 ValidateAudience false, ValidateLifetime true, ValidateIssuerSigningKey true, IssuerSigningKey new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration[Jwt:Key] ?? this-is-a-very-secure-key-for-dev)) }; });注意这个Jwt:Key配置项——它来自appsettings.Development.json值是硬编码的开发密钥。这不是漏洞是设计生产环境必须通过环境变量或Azure Key Vault注入而这个项目留出了明确的替换入口。很多新手翻车就翻在这里直接把密钥写死上线或者根本没配密钥导致401永远返回。2.2 WebApi.Client不是简单new HttpClient()而是类型安全的契约消费者WebApi.Client是一个独立的类库项目.csproj类型为Project SdkMicrosoft.NET.Sdk它不包含任何UI只做一件事把WebApi.Server的OpenAPI定义转换成强类型的C#调用代码。它依赖PackageReference IncludeRefit Version6.3.2 / PackageReference IncludeMicrosoft.Extensions.Http Version6.0.0 / PackageReference IncludeSystem.Net.Http.Json Version6.0.0 /核心是 Refit —— 这个库不是“又一个HTTP客户端”而是基于接口定义生成实现的契约驱动工具。打开IApiService.cspublic interface IApiService { [Get(/api/values)] TaskListValueDto GetValuesAsync([Header(Authorization)] string authHeader); [Post(/api/values)] TaskValueDto CreateValueAsync( [Body] ValueDto value, [Header(Authorization)] string authHeader); }看到没没有字符串拼URL没有手动序列化JSON没有HttpResponseMessage.IsSuccessStatusCode判断。Refit 在编译时通过Source Generator就生成了完整实现调用时直接await apiService.GetValuesAsync(Bearer xxx)失败时自动抛出ApiException且ApiException.Content就是反序列化后的错误响应体比如{ error: Invalid token }。注意Refit 的[Header]参数必须显式传入它不会自动从IHttpClientFactory的默认头中读取。这是很多人踩坑的点——以为配了DefaultRequestHeaders就万事大吉结果401满天飞。2.3 两个项目如何咬合靠OpenAPI DTO 接口契约三件套它们之间没有项目引用也没有共享DLL。咬合方式是WebApi.Server启动后访问/swagger/v1/swagger.json得到 OpenAPI 3.0 JSON 文档WebApi.Client项目中有一个openapi-generator-cli脚本见scripts/generate-client.sh或 Windows 批处理执行命令npx openapitools/openapi-generator-cli generate \ -i http://localhost:5000/swagger/v1/swagger.json \ -g csharp-refit \ -o ./GeneratedClient \ --additional-propertiespackageNameWebApi.Client.Generated生成的GeneratedClient目录下会产出IApiService.cs、ValueDto.cs等文件内容与服务端完全一致WebApi.Client的IApiService接口再用 Refit 注册进 DI 容器builder.Services.AddRefitClientIApiService() .ConfigureHttpClient(c c.BaseAddress new Uri(http://localhost:5000/));这才是真正的“契约优先”服务端改了DTO字段名客户端生成代码就报错编译不过服务端加了新Endpoint客户端生成后自动多出方法。比“口头约定字段名”或“复制粘贴JSON样例”靠谱十倍。3. 快速跑起来5分钟完成本地联调验证别急着改代码先让两个项目在你机器上真正跑通。这是后续所有改造的前提。整个过程不需要改任何一行源码只靠配置和启动顺序。3.1 环境准备确认.NET 6 SDK已就位打开终端执行dotnet --list-sdks确保输出中包含类似6.0.400 [C:\Program Files\dotnet\sdk]的条目。如果没有请去 .NET 6 Download 下载SDK选x64 Runtime SDK不是仅Runtime。注意不要装.NET 7/8这个项目锁定了6.0版本不匹配会导致编译失败或运行时异常。3.2 启动服务端观察Swagger UI是否可用进入WebApi.Server目录cd WebApi.Server dotnet run你应该看到控制台输出类似info: Microsoft.Hosting.Lifetime[14] Now listening on: https://localhost:5001 info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5000 info: Microsoft.Hosting.Lifetime[0] Application started. Press CtrlC to shut down.打开浏览器访问http://localhost:5000/swagger。如果看到交互式API文档页面且能点击GET /api/values的Try it out并成功返回[{ id: 1, name: value1 }]说明服务端已就绪。提示如果你看到ERR_CONNECTION_REFUSED大概率是端口被占用。修改Properties/launchSettings.json中的applicationUrl比如改成http://localhost:5002然后重新dotnet run。3.3 生成并启动客户端用ConsoleApp验证调用链路WebApi.Client本身是类库不能直接运行。项目里附带了一个WebApi.ConsoleClient控制台项目独立的.csproj专门用于测试。先确保服务端已在运行端口5000然后进入WebApi.ConsoleClient目录cd ../WebApi.ConsoleClient dotnet run你会看到控制台逐行打印✅ 正在获取Token... ✅ 已获取Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ✅ 正在调用 GET /api/values... ✅ 获取到3个值: [value1, value2, value3] ✅ 正在调用 POST /api/values... ✅ 创建成功ID: 4这说明JWT Token生成与验证通路正常IApiService.GetValuesAsync()调用成功且Refit自动将JSON数组反序列化为ListValueDtoCreateValueAsync()成功提交并返回新实体全程无手动JSON序列化/反序列化代码。注意WebApi.ConsoleClient的Program.cs中Token是硬编码的Bearer eyJhb...这只是演示。实际项目中Token应由登录接口返回并缓存。这个控制台项目故意简化只为验证“调用链路”。3.4 关键验证点抓包确认HTTP细节光看控制台输出不够。打开Fiddler或浏览器开发者工具Network Tab刷新http://localhost:5000/swagger页面找到GET /swagger/v1/swagger.json请求确认响应状态码是200且Content-Type为application/json。这是客户端生成代码的源头。再回到WebApi.ConsoleClient运行时用Fiddler捕获其发出的请求GET http://localhost:5000/api/valuesHeaders中必须有Authorization: Bearer xxxPOST http://localhost:5000/api/valuesBody是标准JSON如{id:0,name:test}如果任一请求出现401检查服务端appsettings.Development.json中的Jwt:Key是否与客户端生成Token时用的密钥一致如果出现400检查客户端传入的ValueDto是否有必填字段为空服务端用[Required]校验。这一步是建立信任的关键你知道每一行C#代码背后真实发出了什么HTTP请求收到了什么响应。没有黑匣子。4. 常见问题排查那些让你怀疑人生的5分钟这两个项目看似简单但在真实开发机上总有些“玄学”问题让第一次运行的人卡住。以下是我在三台不同配置的Windows机器、一台Mac M1和一台WSL2上实测踩出的坑按发生频率排序每一条都附带可立即验证的解决步骤。4.1 现象Swagger UI打不开显示“Failed to load API definition”原因服务端未启用Swagger中间件或路径配置错误。常见于手动修改过Program.cs却漏掉某行。解决检查WebApi.Server/Program.cs确认存在以下两段代码顺序不能错// 在 builder.Services.AddControllers() 之后builder.Build() 之前 builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 在 var app builder.Build(); 之后 if (app.Environment.IsDevelopment()) { app.UseSwagger(); // ← 必须有 app.UseSwaggerUI(c c.SwaggerEndpoint(/swagger/v1/swagger.json, WebApi.Server v1)); // ← endpoint路径必须是 /swagger/v1/swagger.json }验证直接访问http://localhost:5000/swagger/v1/swagger.json应该返回纯JSON。如果404说明UseSwagger()没生效如果返回HTML说明路径映射错了。4.2 现象ConsoleClient运行时报HttpRequestException: Response status code does not indicate success: 401 (Unauthorized)原因客户端传入的Token无效但更常见的是服务端JWT配置中的ValidateIssuer和ValidateAudience为true而Token里没填Issuer/Audience字段。解决打开WebApi.Server/appsettings.Development.json将Jwt节点改为Jwt: { Key: this-is-a-very-secure-key-for-dev, Issuer: WebApi.Server, Audience: WebApi.Client }然后修改WebApi.Server/Controllers/ValuesController.cs中生成Token的代码通常在Login方法里确保new JwtSecurityToken(...)构造函数中传入了issuer和audience参数。血泪经验宁可开发期关掉Issuer/Audience校验设为false也别在Token里硬编码错误的Issuer值否则401会静默失败连日志都不打。4.3 现象Refit生成的IApiService方法调用时抛NotSupportedException: Cannot access a closed Stream原因Refit默认使用HttpClient的SendAsync但某些网络环境如公司代理会提前关闭Stream。本质是Refit的默认HttpMessageHandler不兼容。解决在WebApi.Client的DI注册处显式指定SocketsHttpHandlerbuilder.Services.AddRefitClientIApiService() .ConfigureHttpClient(c c.BaseAddress new Uri(http://localhost:5000/)) .AddHttpMessageHandler(() new SocketsHttpHandler { PooledConnectionLifetime TimeSpan.FromMinutes(5), KeepAlivePingDelay TimeSpan.FromSeconds(30), KeepAlivePingTimeout TimeSpan.FromSeconds(30) });注意此Handler需引用System.Net.Http命名空间且仅在.NET 6有效。这是Refit 6.x的已知行为不是Bug。4.4 现象dotnet run报错The framework Microsoft.NETCore.App, version 6.0.0 was not found原因只安装了.NET 6 Runtime没装SDK。Runtime只能运行已编译的程序SDK才能编译源码。解决卸载所有.NET版本重新下载安装.NET 6 SDK不是Runtime。安装后重启终端再执行dotnet --list-sdks确认。提示VS2022自带SDK但如果你用VS Code必须单独装SDK。别信“我有VS2022所以不用装”的说法VS的SDK路径不一定被CLI识别。4.5 现象Swagger UI里Try it out按钮点击后返回500 Internal Server Error控制台无日志原因服务端启用了Serilog但未配置至少一个Sink如Console导致日志被丢弃错误堆栈看不到。解决检查WebApi.Server/Program.cs确认UseSerilog()之后有.WriteTo.Console()Log.Logger new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() // ← 必须有这一行 .CreateBootstrapLogger();然后重启服务端再点Try it out控制台会立刻打印出完整的Exception StackTrace比如NullReferenceException in ValueService.GetValues()定位快十倍。5. 核心模块代码逐行解析从ValuesController到Refit调用链现在你已经跑通了接下来要真正理解“它怎么工作的”。我们不讲概念直接切进最核心的两段代码服务端的Controller处理逻辑和客户端的Refit调用链。每一行都解释它在做什么、为什么这么写、改哪里会影响什么。5.1 服务端ValuesController.GetValuesAsync —— 一个请求的完整生命周期打开WebApi.Server/Controllers/ValuesController.cs[ApiController] [Route(api/[controller])] public class ValuesController : ControllerBase { private readonly IValueService _valueService; private readonly ILoggerValuesController _logger; public ValuesController(IValueService valueService, ILoggerValuesController logger) { _valueService valueService; _logger logger; } [HttpGet] [ProducesResponseType(typeof(ListValueDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async TaskActionResultListValueDto GetValuesAsync() { _logger.LogInformation(开始处理 GET /api/values 请求); try { var values await _valueService.GetValuesAsync(); // ← 1. 业务逻辑入口 _logger.LogInformation(成功获取 {Count} 个值, values.Count); return Ok(values); // ← 2. 序列化并返回200 } catch (Exception ex) { _logger.LogError(ex, 处理 GET /api/values 时发生未预期异常); return StatusCode(500, new { error 服务内部错误请稍后重试 }); // ← 3. 统一错误包装 } } }逐行说明[ApiController]启用ASP.NET Core的自动模型验证、自动400响应等特性。没有它[Required]校验失败时不会自动返回400。[Route(api/[controller])]路由模板[controller]自动替换为类名去掉Controller后缀即ValuesController→values。构造函数注入IValueService和ILogger遵循依赖注入原则便于单元测试和替换实现比如换成SQL版Service。[ProducesResponseType]Swagger文档生成依据。三个特性分别告诉Swagger“这个方法可能返回200List ”、“也可能返回401”、“还可能返回500”。没有它们Swagger UI里看不到响应示例。try/catch块这是关键。它不捕获具体业务异常如NotFoundException而是捕获所有未处理异常防止堆栈信息泄露给前端。生产环境应记录详细日志但返回给客户端的永远是泛化错误服务内部错误避免暴露服务端技术细节。return Ok(values)Ok()是ActionResultT的语法糖它会自动调用System.Text.Json序列化values为JSON并设置Content-Type: application/json和状态码200。你不需要写JsonConvert.SerializeObject。参数说明GetValuesAsync()方法没有参数所以不需要[FromQuery]或[FromBody]。如果要支持分页可以加[FromQuery] int page 1, int pageSize 10框架会自动绑定查询字符串。5.2 客户端Refit生成的IApiService调用 —— 强类型背后的魔法Refit生成的代码在WebApi.Client/GeneratedClient/IApiService.cs或类似路径。我们看GetValuesAsync的生成逻辑public partial interface IApiService { [Get(/api/values)] TaskListValueDto GetValuesAsync([Header(Authorization)] string authHeader); }Refit在编译时会为这个接口生成一个实现类你不需要看到源码但要知道它长什么样。简化版逻辑如下// 这是Refit生成的伪代码实际更复杂 public class ApiServiceImpl : IApiService { private readonly HttpClient _httpClient; public ApiServiceImpl(HttpClient httpClient) _httpClient httpClient; public async TaskListValueDto GetValuesAsync(string authHeader) { // 1. 构建请求 var request new HttpRequestMessage(HttpMethod.Get, /api/values); request.Headers.Authorization AuthenticationHeaderValue.Parse(authHeader); // ← 自动解析Bearer // 2. 发送请求 var response await _httpClient.SendAsync(request); // 3. 处理响应 if (!response.IsSuccessStatusCode) { // 自动读取响应体尝试反序列化为ErrorDto如果定义了 var errorContent await response.Content.ReadAsStringAsync(); throw new ApiException(response.StatusCode, errorContent, response.RequestMessage); } // 4. 反序列化成功响应体为 ListValueDto var json await response.Content.ReadAsStringAsync(); return JsonSerializer.DeserializeListValueDto(json, new JsonSerializerOptions { PropertyNameCaseInsensitive true // ← 关键允许JSON字段名大小写不敏感 }); } }关键点解析HttpClient实例来自IHttpClientFactory它管理连接池、处理DNS刷新、支持Polly重试策略本项目未启用但留了扩展点。AuthenticationHeaderValue.Parse(authHeader)自动识别Bearer xxx或Basic xxx格式无需手动分割字符串。PropertyNameCaseInsensitive true这是救命设置。服务端返回{id:1,name:test}客户端DTO是public record ValueDto(int Id, string Name)首字母大写。没有这个选项反序列化会失败因为JSON字段id不匹配C#属性Id。Refit默认开启此项但如果你手写JsonSerializer必须显式设置。ApiExceptionRefit定义的异常类型它包含原始HTTP状态码、响应体字符串、请求信息。你可以catch (ApiException ex) if (ex.StatusCode HttpStatusCode.Unauthorized)做精确处理而不是笼统的catch (Exception)。改造提示如果你想在每次请求前自动添加Token而不是每次调用都传authHeader可以创建一个DelegatingHandlerpublic class AuthHeaderHandler : DelegatingHandler { protected override async TaskHttpResponseMessage SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { request.Headers.Authorization new AuthenticationHeaderValue(Bearer, your-token); return await base.SendAsync(request, cancellationToken); } }然后在DI中注册services.AddTransientAuthHeaderHandler(); services.AddRefitClientIApiService().AddHttpMessageHandlerAuthHeaderHandler();6. 二次开发实战从“能跑”到“能用”的3个关键改造跑通只是起点。真正让这套资源在你项目中扎根需要做三件小事一是把内存数据换成真实数据库二是让客户端支持Token自动刷新三是把DTO生成自动化集成进CI/CD。这三步做完你就从“使用者”变成了“架构维护者”。6.1 改造1用SQL Server替代In-Memory Database5分钟WebApi.Server当前用AddInMemoryDatabase是为了零依赖但生产必须换。步骤极简在WebApi.Server.csproj中移除Microsoft.EntityFrameworkCore.InMemory包引用添加PackageReference IncludeMicrosoft.EntityFrameworkCore.SqlServer Version6.0.26 / PackageReference IncludeMicrosoft.EntityFrameworkCore.Tools Version6.0.26 PrivateAssetsall/PrivateAssets IncludeAssetsruntime; build; native; contentfiles; analyzers; buildtransitive/IncludeAssets /PackageReference修改Program.cs中的DbContext注册// 替换原来的 AddInMemoryDatabase builder.Services.AddDbContextAppDbContext(options options.UseSqlServer(builder.Configuration.GetConnectionString(DefaultConnection)));在appsettings.Development.json中添加连接字符串ConnectionStrings: { DefaultConnection: Server(localdb)\\mssqllocaldb;DatabaseWebApiDemo;Trusted_Connectiontrue;MultipleActiveResultSetstrue }创建Data/AppDbContext.cspublic class AppDbContext : DbContext { public AppDbContext(DbContextOptionsAppDbContext options) : base(options) { } public DbSetValueEntity Values { get; set; } } public class ValueEntity { public int Id { get; set; } public string Name { get; set; } string.Empty; }执行迁移在WebApi.Server目录dotnet ef migrations add InitialCreate dotnet ef database update验证重启服务端访问http://localhost:5000/api/values应该返回空数组[]因为新库是空的。用SQL Server Management Studio连接(localdb)\mssqllocaldb确认WebApiDemo数据库和Values表已创建。这就是“可落地”的第一步数据持久化。6.2 改造2客户端自动Token刷新10分钟当前WebApi.ConsoleClient的Token是硬编码的实际项目中Token会过期。Refit本身不处理刷新但可以结合IHttpClientFactory的AddPolicyHandler实现。在WebApi.Client项目中添加Polly包PackageReference IncludeMicrosoft.Extensions.Http.Polly Version6.0.0 / PackageReference IncludePolly Version7.2.3 /创建TokenRefreshPolicy.cspublic class TokenRefreshPolicy { private readonly IServiceProvider _serviceProvider; public TokenRefreshPolicy(IServiceProvider serviceProvider) _serviceProvider serviceProvider; public AsyncRetryPolicyHttpResponseMessage Create() { return PolicyHttpResponseMessage .HandleResult(r r.StatusCode HttpStatusCode.Unauthorized) .WaitAndRetryAsync( retryCount: 1, sleepDurationProvider: retryAttempt TimeSpan.FromMilliseconds(100), onRetry: (outcome, timespan, retryCount, context) { // 这里调用登录接口刷新Token var loginService _serviceProvider.GetRequiredServiceILoginService(); loginService.RefreshTokenAsync().Wait(); }); } }在WebApi.ConsoleClient/Program.cs的DI注册中builder.Services.AddSingletonTokenRefreshPolicy(); builder.Services.AddRefitClientIApiService() .ConfigureHttpClient(c c.BaseAddress new Uri(http://localhost:5000/)) .AddPolicyHandler(provider provider.GetRequiredServiceTokenRefreshPolicy().Create());注意ILoginService需要你自己实现调用/api/auth/login获取新Token并存入内存或SecureString。这个策略会在收到401时自动重试一次请求期间刷新Token避免上层代码到处写if (401) then login()。6.3 改造3CI/CD中自动化OpenAPI生成5分钟每次服务端改了API都要手动运行openapi-generator-cli生成客户端太原始。把它塞进CI流程在WebApi.Server项目根目录创建generate-openapi.shLinux/Mac或generate-openapi.batWindows# generate-openapi.bat echo off echo 正在从服务端生成OpenAPI文档... curl -s http://localhost:5000/swagger/v1/swagger.json -o ./openapi.json echo 文档已保存为 openapi.json在WebApi.Client目录创建update-client.sh# update-client.sh npx openapitools/openapi-generator-cli generate \ -i ../WebApi.Server/openapi.json \ -g csharp-refit \ -o ./GeneratedClient \ --additional-propertiespackageNameWebApi.Client.Generated在GitHub Actions.github/workflows/build.yml中添加步骤- name: Generate OpenAPI spec run: cd WebApi.Server dotnet run timeout 10 curl -s http://localhost:5000/swagger/v1/swagger.json -o openapi.json - name: Update client SDK run: cd WebApi.Client bash update-client.sh - name: Commit generated files run: | git config --local user.email actiongithub.com git config --local user.name GitHub Action git add WebApi.Client/GeneratedClient git commit -m chore: update client SDK from OpenAPI || echo No changes to commit这样每次WebApi.Server的PR合并到main分支CI就会自动拉取最新OpenAPI更新客户端代码并提交。你的团队再也不用担心“接口变了客户端忘了改”。从那以后我每次新建一个WebApi项目都会先拷贝这套骨架然后花15分钟做完这三步改造——换数据库、加Token刷新、接CI生成。它让我跳过了90%的“基础建设陷阱”把精力真正放在业务逻辑上。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站