.NET WebAPI无请求体时如何使用模型默认值?
问题描述
使用.NET WebAPI开发接口,期望用户无需携带任何请求头和请求体调用https://localhost:7222/data时,控制器能自动使用带有默认值(valueOne为true)的BodyParams模型并返回响应,但目前调用时持续收到415(不支持的媒体类型)响应。
通过dotnet new webapi创建新项目,添加的控制器和模型类如下:
[HttpGet("/data")] public async Task<IActionResult> GetModel(BodyParams input) { var response = new { Message = "Hello", value = input.valueOne }; return Ok(response); } public class BodyParams { public bool valueOne { get; set; } = true; }
已尝试以下方案但均无效:
- 添加
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]:仅在请求体为空JSON对象{}时生效,不符合无请求体的需求; - 将
BodyParams设为可空类型:无任何变化; - 配置
.AddControllers(opt => opt.AllowEmptyInputInBodyModelBinding = true):无任何变化; - 参考Stack Overflow方案:响应变为400错误,提示
"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true."; - 尝试手动设置
context.HttpContext.Request.Body为空JSON对象:实现过于复杂,未采用。
解决方案
方案一:改用查询参数绑定(推荐,最简洁)
GET请求的设计规范通常不从请求体获取参数,且.NET WebAPI默认不会为GET请求绑定请求体参数。直接将模型绑定方式改为查询参数绑定,即可实现无请求体时自动使用默认值。
修改控制器方法,给input参数添加[FromQuery]特性:
[HttpGet("/data")] public async Task<IActionResult> GetModel([FromQuery] BodyParams input) { var response = new { Message = "Hello", value = input.valueOne }; return Ok(response); }
此时用户调用https://localhost:7222/data时,框架会自动实例化BodyParams并使用默认的valueOne=true;如果需要自定义值,也可以通过https://localhost:7222/data?valueOne=false传递参数。
方案二:自定义模型绑定器(需从请求体绑定GET请求时使用)
如果业务场景必须从请求体获取GET请求的参数,可通过自定义模型绑定器处理空请求体的情况,自动返回带默认值的模型实例。
- 创建自定义模型绑定器:
public class BodyParamsModelBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); // 检测请求体是否为空或不可读 if (bindingContext.HttpContext.Request.ContentLength == 0 || !bindingContext.HttpContext.Request.Body.CanRead) { // 返回带默认值的模型实例 bindingContext.Result = ModelBindingResult.Success(new BodyParams()); return; } // 若存在请求体,使用默认JSON序列化逻辑绑定 var jsonOptions = bindingContext.HttpContext.RequestServices .GetRequiredService<IOptions<JsonOptions>>().Value; var model = await JsonSerializer.DeserializeAsync<BodyParams>( bindingContext.HttpContext.Request.Body, jsonOptions.JsonSerializerOptions); // 若反序列化结果为空,仍返回默认实例 bindingContext.Result = ModelBindingResult.Success(model ?? new BodyParams()); } }
- 给
BodyParams类绑定自定义模型绑定器:
[ModelBinder(BinderType = typeof(BodyParamsModelBinder))] public class BodyParams { public bool valueOne { get; set; } = true; }
- 控制器方法保持原有写法即可:
[HttpGet("/data")] public async Task<IActionResult> GetModel(BodyParams input) { var response = new { Message = "Hello", value = input.valueOne }; return Ok(response); }
方案说明
之前的方案无效的原因:
- 415错误本质是GET请求无请求体时,框架无法识别请求的媒体类型(没有Content-Type头);
[FromBody]特性要求请求体必须包含合法JSON内容,空请求体(无任何数据)不符合JSON格式规范,即使配置EmptyBodyBehavior.Allow也仅支持空JSON对象{};AllowEmptyInputInBodyModelBinding配置仅适用于存在请求体(哪怕是空内容)的POST/PUT等请求,对完全无请求体的GET请求不生效。
内容的提问来源于stack exchange,提问作者Gary Frewin
相关产品推荐
相关产品推荐

