如何让NSwag生成支持IAsyncEnumerable端点的客户端?
问题描述
我开发了一个返回IAsyncEnumerable<DayModel>的API端点,代码如下:
[HttpPost("GetByDates")] [ProducesResponseType(typeof(IAsyncEnumerable<DayModel>), StatusCodes.Status200OK)] public async IAsyncEnumerable<DayModel> GetByDates([FromBody] DayModelGetByDatesRequest request) { await foreach (var dayModel in _dayService.GetAsync(request.channelGuid, request.dates.ToArray(), request.onlyPublished, request.IncludeDiscardedScheduledItems)) { yield return dayModel; }; }
当前NSwag生成的Swagger Schema将该端点的响应识别为数组,内容如下:
"/Private/Days/GetByDates": { "post": { "tags": [ "Days" ], "operationId": "Days_GetByDates", "requestBody": { "x-name": "request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DayModelGetByDatesRequest" } } }, "required": true, "x-position": 1 }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Day" } } } } } } } }
我的NSwag配置如下:
services.AddOpenApiDocument(configure => { configure.Title = "MyAppName (Private)"; configure.DocumentName = "private"; configure.SchemaType = SchemaType.OpenApi3; configure.SchemaNameGenerator = new CustomNameGenerator(); configure.AddOperationFilter(new RequireUserHeaderParameterFilter().Process); configure.AddSecurity("Bearer", new OpenApiSecurityScheme() { In = OpenApiSecurityApiKeyLocation.Header, Description = "Please enter the word \"Bearer\" followed by space and token", Name = "Authorization", Type = OpenApiSecuritySchemeType.ApiKey, }); configure.ApiGroupNames = new string[] { "Private" }; });
另一个项目通过该Schema生成的客户端使用Newtonsoft.Json而非System.Text.Json,代码示例如下:
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TablaApiClient { private string _baseUrl = "https://localhost:5102"; private System.Net.Http.HttpClient _httpClient; private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings; public TablaApiClient(System.Net.Http.HttpClient httpClient) { _httpClient = httpClient; _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings); } private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() { var settings = new Newtonsoft.Json.JsonSerializerSettings(); UpdateJsonSerializerSettings(settings); return settings; } public string BaseUrl { get { return _baseUrl; } set { _baseUrl = value; } } protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } } partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); }
目前该端点并未序列化IAsyncEnumerable,而是返回ICollection,现咨询两个问题:
- 如何让生成的客户端正确识别IAsyncEnumerable端点,实现流式处理而非使用全缓冲集合?
- 如何让Swagger改用System.Text.Json替代Newtonsoft.Json?
解决方案
1. 让客户端支持IAsyncEnumerable流式处理
OpenAPI规范无原生流式响应定义,NSwag默认将IAsyncEnumerable识别为数组,需通过自定义标记和配置实现流式支持:
步骤1:标记API端点为流式响应
修改API方法,添加自定义扩展标记明确这是流式响应:
[HttpPost("GetByDates")] [Produces("application/json")] [ProducesResponseType(typeof(IAsyncEnumerable<DayModel>), StatusCodes.Status200OK)] // 添加自定义扩展,告知NSwag这是流式响应 [OpenApiOperationExtension("x-stream", true)] public async IAsyncEnumerable<DayModel> GetByDates([FromBody] DayModelGetByDatesRequest request) { await foreach (var dayModel in _dayService.GetAsync(request.channelGuid, request.dates.ToArray(), request.onlyPublished, request.IncludeDiscardedScheduledItems)) { yield return dayModel; } }
步骤2:添加NSwag操作过滤器修改Swagger Schema
创建自定义过滤器,检测流式标记并更新响应配置:
public class StreamResponseOperationFilter : IOperationFilter { public void Process(OpenApiOperation operation, OperationProcessorContext context) { var isStream = context.MethodInfo.GetCustomAttribute<OpenApiOperationExtensionAttribute>()?.Arguments.FirstOrDefault() as bool?; if (isStream == true) { // 添加分块传输头标记 operation.Responses["200"].Headers.Add("Transfer-Encoding", new OpenApiHeader { Description = "Chunked transfer for streaming response", Schema = new OpenApiSchema { Type = "string", Default = new OpenApiString("chunked") } }); // 给Schema添加流式扩展,供客户端生成工具识别 var responseSchema = operation.Responses["200"].Content["application/json"].Schema; responseSchema.Extensions.Add("x-stream", new OpenApiBoolean(true)); } } }
在NSwag配置中注册过滤器:
services.AddOpenApiDocument(configure => { // 原有配置... configure.AddOperationFilter<StreamResponseOperationFilter>(); });
步骤3:配置客户端生成启用流式支持
- NSwagStudio:进入「Code Generation」→「C#」→「Advanced」,勾选「Generate async enumerable for stream responses」
- nswag.json配置文件:添加以下设置
"codeGenerators": { "csharp": { "generateAsyncEnumerableForStreamResponses": true, // 其他配置... } }
生成后的客户端方法会返回IAsyncEnumerable<DayModel>,实现流式处理。
2. 切换NSwag到System.Text.Json
步骤1:安装System.Text.Json相关NSwag包
替换原有NSwag.AspNetCore包:
dotnet add package NSwag.AspNetCore.SystemTextJson
步骤2:修改NSwag配置使用System.Text.Json生成Schema
services.AddOpenApiDocument(configure => { // 原有配置... // 指定使用System.Text.Json Schema生成器 configure.SchemaGenerator = new SystemTextJsonSchemaGenerator(new SystemTextJsonSchemaGeneratorSettings { SerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, IgnoreNullValues = true // 其他序列化配置 } }); });
步骤3:客户端生成时指定System.Text.Json
- NSwagStudio:进入「Code Generation」→「C#」→「Json」,选择「System.Text.Json」
- nswag.json配置文件:添加以下设置
"codeGenerators": { "csharp": { "jsonLibrary": "SystemTextJson", // 其他配置... } }
生成的客户端会自动使用System.Text.Json替代Newtonsoft.Json。
内容的提问来源于stack exchange,提问作者Olaf Dlugosz
相关产品推荐
相关产品推荐

