.NET中使用NSwag调用无请求体POST接口时触发FormatException
问题描述
后端定义了一个无请求体的POST接口:
[HttpPost] [Route("AddUser/{id}")] [EnableCors] [AllowAnonymous] public async Task<ActionResult<Response>> AddUser([FromRoute] string id)
通过NSwag生成客户端代码后,swaggerclient.cs中会自动添加以下代码:
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json;charset=utf-8");
执行该行代码时会触发FormatException。
原因分析
问题核心在于:空字符串不是有效的JSON格式,但生成的代码将空字符串以application/json的Content-Type发送给后端。当后端尝试解析这个请求体时,会因为无法将空字符串解析为合法JSON而抛出FormatException。
NSwag早期版本对OpenAPI规范的处理存在偏差——按照规范,无请求体的POST接口不应包含requestBody字段,但NSwag会默认添加空的请求体定义,进而生成错误的客户端代码。
解决方法
方法1:配置NSwag避免生成空请求体代码
在NSwag生成配置中添加规则,让无请求体的POST接口不生成请求体相关代码:
- 使用NSwag CLI或配置文件时,设置
RequestBodyRequired为false,或针对POST场景额外配置忽略空请求体; - 使用NSwag.AspNetCore时,在
AddOpenApiDocument中手动标记目标接口无请求体:
services.AddOpenApiDocument(settings => { settings.OperationProcessors.Add(new OperationProcessor(context => { if (context.OperationDescription.Path == "/AddUser/{id}" && context.OperationDescription.HttpMethod == HttpMethod.Post) { context.OperationDescription.Operation.RequestBody = null; } return true; })); });
方法2:通过Swashbuckle自定义过滤器修改接口定义
直接添加[Consumes]标签在无[FromBody]参数时无法被Swashbuckle识别,可通过自定义过滤器移除无请求体接口的请求体定义:
services.AddSwaggerGen(c => { c.OperationFilter<RemoveEmptyRequestBodyFilter>(); }); public class RemoveEmptyRequestBodyFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var hasBodyParameter = context.MethodInfo.GetParameters().Any(p => p.GetCustomAttributes(typeof(FromBodyAttribute), false).Any()); if (!hasBodyParameter && operation.RequestBody != null) { operation.RequestBody = null; } } }
该过滤器会自动清理所有无[FromBody]参数接口的请求体定义,NSwag据此生成客户端时就不会添加空请求体代码。
方法3:临时修改生成的客户端代码(不推荐)
手动删除生成代码中设置request_.Content的行,但此方法每次重新生成客户端都会覆盖修改,仅适用于临时测试场景。
关于NSwag规范处理的说明
该问题属于NSwag早期版本的实现缺陷,后续版本已针对此类场景做了优化。如果使用较新版本的NSwag,可通过配置GenerateEmptyRequestBodyForPost为false直接避免生成空请求体代码。
另外,[Consumes]标签仅在接口存在[FromBody]参数时才会被Swashbuckle识别,因此无请求体的接口无法通过该标签直接修改Swagger定义,需通过自定义过滤器处理。
内容的提问来源于stack exchange,提问作者SepanD

