C# API中POST上的模型绑定问题
我很难理解Asp.NETCore2中的模型绑定过程。我有一个非常简单的API,它有一个模型。它有一些基本的验证。每当用户发布错误的模型时,我都会尝试返回422 unprocessableentity以及来自modelstate的错误消息 我想了解的两个问题如下:C# API中POST上的模型绑定问题,c#,asp.net-core,C#,Asp.net Core,我很难理解Asp.NETCore2中的模型绑定过程。我有一个非常简单的API,它有一个模型。它有一些基本的验证。每当用户发布错误的模型时,我都会尝试返回422 unprocessableentity以及来自modelstate的错误消息 我想了解的两个问题如下: services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
- 如果我发布了一个没有ID的请求,则会绕过必需的属性创建一个默认ID 0。我假设这是为字段提供默认值的C#功能。有没有办法避免这种情况
- 另一个问题是,如果我在post操作中放置一个断点并发送一个错误的请求,它甚至不会进入该方法。它使用验证属性发回400个错误请求。这是怎么回事?请求是否在尝试将模型绑定到无效属性(即名称长度>10)时立即停止?我需要它做的是发回一个422不可处理的实体,带有相同的错误消息,而不是400
public void ConfigureServices(IServiceCollection services)
{
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc();
services.AddSingleton<IItemRepository, ItemRepository>();
}
值控制器.cs
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
private IItemRepository _context;
public ValuesController(IItemRepository context)
{
_context = context;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return Ok(_context.Items);
}
// GET api/values/5
[HttpGet("{id}", Name = "GetSingle")]
public ActionResult<string> Get(int id)
{
return Ok(_context.Items.Where(x => x.Id == id));
}
// Problem here - placing a breakpoint in below method does not do anytthing as it will return a 400 bad request instead of 422
[HttpPost]
public ActionResult Post([FromBody] ItemModel itemModel)
{
if (!ModelState.IsValid)
{
return new UnprocessableEntityObjectResult(ModelState);
}
ItemModel addNew = new ItemModel { Id = itemModel.Id, Name = itemModel.Name };
_context.AddValue(addNew);
return Ok(addNew);
}
}
[路由(“api/[控制器]”)]
[ApiController]
公共类值控制器:控制器
{
私有IItemRepository_上下文;
公共价值控制器(IItemRepository上下文)
{
_上下文=上下文;
}
//获取api/值
[HttpGet]
公共行动结果获取()
{
返回Ok(_context.Items);
}
//获取api/values/5
[HttpGet(“{id}”,Name=“GetSingle”)]
公共操作结果获取(int-id)
{
返回Ok(_context.Items.Where(x=>x.Id==Id));
}
//这里的问题-在下面的方法中放置断点不会执行任何操作,因为它将返回一个400错误请求,而不是422错误请求
[HttpPost]
公共操作结果发布([FromBody]ItemModel ItemModel)
{
如果(!ModelState.IsValid)
{
返回新的不可处理EntityObjectResult(ModelState);
}
ItemModel addNew=newitemmodel{Id=ItemModel.Id,Name=ItemModel.Name};
_AddValue(addNew);
返回Ok(新增);
}
}
如果我发布一个没有ID的请求,将创建一个默认ID 0
绕过必需的属性。我假设这是C#
为字段提供默认值的功能。有办法吗
为了避免这种情况
如前所述,您需要将模型更改为
public class ItemModel
{
[Required]
public int? Id { get; set; }
[MaxLength(10)]
public string Name { get; set; }
}
另一个问题是,如果在post操作中放置断点
并且发送一个错误的请求,它甚至没有进入方法。它发出
使用验证属性返回400错误请求。怎么做
这工作?请求是否在尝试模型绑定时立即停止
无效属性(即名称长度>10)?我需要它做的是
发回带有相同错误消息的422不可处理实体
而不是400
这是因为您将ApiControllerAttribute
应用于控制器:
验证错误会自动触发HTTP 400响应。以下代码在您的操作中变得不必要:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
您可以删除该属性,或者按照同一链接的说明,将其添加到启动配置中:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
})
services.Configure(选项=>
{
options.SuppressModelStateInvalidFilter=true;
})
您的第一个问题可以通过使属性为空来解决。正如Stepen Muecke所评论的那样
另外请看一看,BindRequired属性可能会有所帮助。本文还介绍了如何调整行为
对于第二个问题,这是Asp.NETCore2.1的(破坏性)行为。新的是新的。这就解释了为什么没有命中断点。您可以按如下方式抑制此操作:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
services.Configure(选项=>
{
options.SuppressModelStateInvalidFilter=true;
});
对于您的第一个问题,如果您不想使属性为空,您也可以放置一个范围属性[range(1,int.MaxValue)],但在这种情况下,0将不是有效值
对于第二个问题,如果仍然希望从ApiControllerAttribute进行自动模型验证,但希望使用422响应代码而不是400,则可以使用InvalidModelStateResponseFactory配置选项
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = ctx =>
new UnprocessableEntityObjectResult(ctx.ModelState);
});
services.Configure(选项=>
{
options.InvalidModelStateResponseFactory=ctx=>
新的不可处理EntityObjectResult(ctx.ModelState);
});
发送错误请求
能否向我们展示错误请求的示例?请将您的Id
属性(视图模型)设置为空且[必需]
-谢谢您和@StephenMuecke。这回答了我的问题。我没有意识到,使用新的apicontroller属性,它会自动触发错误的请求。在我的ID上使用可空类型修复了这个问题。这更全面地回答了这个问题。
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = ctx =>
new UnprocessableEntityObjectResult(ctx.ModelState);
});