You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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,现咨询两个问题:

  1. 如何让生成的客户端正确识别IAsyncEnumerable端点,实现流式处理而非使用全缓冲集合?
  2. 如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:03:08