ASP.NET Core ABP项目Swashbuckle生成Swagger报StringBuilder内存溢出
Swashbuckle生成Swagger JSON内存溢出问题处理方案
报错信息
RequestAborted: Exception of type 'System.OutOfMemoryException' was thrown.. Request Path: /swagger/v1/swagger.json System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown. at System.Text.StringBuilder.ToString() at System.IO.StringWriter.ToString() at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.RespondWithSwaggerJson(HttpResponse response, OpenApiDocument swagger) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context) at Abp.AspNetZeroCore.Web.Authentication.JwtBearer.JwtTokenMiddleware.<>c__DisplayClass0_0.<<UseJwtTokenMiddleware>b__0>d.MoveNext()
触发原因
- 单Swagger文档体积过大:项目接口数量多、参数模型嵌套层级深、泛型模型滥用,序列化后的JSON体积过大,
StringBuilder.ToString()需要申请连续内存块,托管堆无法提供足够连续空间时就会抛出OOM,32位进程下因用户态内存上限仅2G更容易触发该问题。 - Swashbuckle旧版本设计缺陷:低版本Swashbuckle生成JSON时会先将全量内容写入StringWriter,再一次性转字符串输出到响应流,大文档场景下内存占用极高。
- ABP框架自动暴露接口的特性:ASP.NET Boilerplate默认会将所有应用服务方法自动注册为API接口,未做过滤时会将大量内部接口、ABP内置接口也加入Swagger文档,进一步放大文档体积。
排查步骤
- 统计当前暴露的API总数:如果可偶尔正常打开swagger.json,直接统计
paths节点的接口数量,单文档超过1000个接口就属于高风险场景。 - 确认进程运行位数:检查项目是否运行在32位进程模式下,32位进程的内存上限极易触发OOM。
- 排查模型循环引用:检查接口入参出参模型是否存在循环依赖(如A引用B、B又引用A),循环引用会导致Swashbuckle序列化时无限展开,内存暴涨。
解决方法
1. 过滤不需要暴露的接口
通过DocInclusionPredicate配置过滤规则,排除ABP内置接口、内部非公开接口:
services.AddSwaggerGen(options => { options.DocInclusionPredicate((docName, apiDesc) => { // 过滤ABP内置接口、内部业务接口 if (apiDesc.RelativePath.StartsWith("api/abp/") || apiDesc.RelativePath.StartsWith("api/internal/")) { return false; } // 补充自定义过滤逻辑 return true; }); });
2. 拆分多分组Swagger文档
按业务模块、接口使用方拆分多个独立的Swagger文档,避免单个JSON体积过大:
services.AddSwaggerGen(options => { options.SwaggerDoc("v1-admin", new OpenApiInfo { Title = "管理端接口", Version = "v1" }); options.SwaggerDoc("v1-client", new OpenApiInfo { Title = "客户端接口", Version = "v1" }); // 搭配DocInclusionPredicate给不同分组分配对应接口 });
3. 升级Swashbuckle版本启用流式输出
将Swashbuckle.AspNetCore升级到5.x以上适配.NET Core 3.1的版本,启用流式序列化直接输出到响应流,避免StringBuilder的内存占用:
app.UseSwagger(options => { options.RouteTemplate = "swagger/{documentName}/swagger.json"; options.SerializeAsV2 = false; });
4. 配置模型忽略规则
自定义Schema过滤器,忽略不需要在Swagger中展示的内部模型、敏感属性:
services.AddSwaggerGen(options => { options.SchemaFilter<IgnorePropertySchemaFilter>(); }); public class IgnorePropertySchemaFilter : ISchemaFilter { public void Apply(OpenApiSchema schema, SchemaFilterContext context) { // 忽略指定内部模型 if (context.Type == typeof(内部通用模型类)) { schema.Properties.Clear(); } // 忽略标记了自定义[SwaggerIgnore]特性的属性 var ignoredProperties = context.Type.GetProperties() .Where(p => p.GetCustomAttribute<SwaggerIgnoreAttribute>() != null); foreach (var prop in ignoredProperties) { var camelCaseName = char.ToLowerInvariant(prop.Name[0]) + prop.Name.Substring(1); schema.Properties.Remove(camelCaseName); } } }
5. 调整进程运行配置
确保项目运行在64位进程模式下,IIS部署时关闭"启用32位应用程序"选项,自宿主部署时选择x64运行时。
内容的提问来源于stack exchange,提问作者André Haupt
相关产品推荐
相关产品推荐

