Return BadRequest with null required 属性 in model .net core web api
Return BadRequest with null required property in model .net core web api
所以,我的控制器中有一个创建方法:
[Authorize]
[Route("[controller]")]
[ApiController]
public class ConversionsController : ControllerBase
{
private readonly IGenericService<Conversion> _conversionService;
public ConversionsController(IGenericService<Conversion> conversionService) => _conversionService = conversionService;
/// <summary>
/// Creates a new conversion
/// </summary>
/// <param name="conversion">The conversion</param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(Conversion), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync(ConversionViewModel conversion)
{
if (conversion == null) return BadRequest();
if (!ModelState.IsValid) return BadRequest(ModelState);
var request = ModelFactory.Create(conversion);
_conversionService.Create(request);
await _conversionService.SaveChangesAsync();
return Created(nameof(Get), new Sxp.Web.ActionResult<Conversion>(request, string.Format(Resources.EntityCreated, "conversion")));
}
}
如您所见,如果没有模型,它将 return 一个错误的请求(有效)。
但是下一行说明 if (!ModelState.IsValid)
永远不会起作用。它始终 return 有效,即使在 null 处需要 属性。
我写了一个测试,总是失败:
[Test]
public async Task ReturnBadRequestIfNullRequiredProperty()
{
// Assemble
var services = ConversionsControllerContext.GivenServices();
var controller = services.WhenCreateController();
// Act
var actionResult = await controller.CreateAsync(new ConversionViewModel());
var badRequestResult = actionResult as BadRequestResult;
// Assert
badRequestResult.Should().NotBeNull();
badRequestResult?.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
}
正如您在这里看到的,我只是传递了一个 CategoryViewModel
,根本没有设置任何属性,但它失败了。
视图模型如下所示:
public class ConversionViewModel
{
public int Id { get; set; }
[Range(1, int.MaxValue, ErrorMessageResourceName = "RangeErrorMessage", ErrorMessageResourceType = typeof(Resources))] public int FeedId { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string Name { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string FieldName { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string Expression { get; set; }
public double Value { get; set; }
public MathOperator MathOperator { get; set; }
public FilterOperator FilterOperator { get; set; }
}
我唯一能想到的是我禁用了自动状态验证:
.ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; });
我认为这让我可以在控制器中处理它。
有人遇到过这个吗?
如果您使用 services.AddMvcCore
而不是 services.AddMvc
然后添加
services.AddDataAnnotations();
到Startup.ConfigureServices方法
您正在 运行 进行单元测试,而不是端到端集成测试,因此在进行测试时,某些框架问题不会 运行 进行。
这些数据注释属性是仅在 运行 时被框架识别的元数据,而不是在隔离单元测试期间识别的元数据,因为当应用程序处于 运行 时它们实际上被模型绑定器读取宁.
如果目的是让框架验证模型,则需要进行集成测试。
引用Integration tests in ASP.NET Core
否则,被测对象(控制器)必须在安排测试时手动更新其 ModelState
,以便在执行时表现出预期的效果。
通过使用 AddModelError
添加错误来测试无效的模型状态,如下面的测试所示:
[Test]
public async Task ReturnBadRequestIfNullRequiredProperty() {
// Arrange / Assemble
var services = ConversionsControllerContext.GivenServices();
var controller = services.WhenCreateController();
controller.ModelState.AddModelError("Name","Name required"); //<-- Invalidate model state
//...other desired errors.
// Act
var actionResult = await controller.CreateAsync(new ConversionViewModel());
var badRequestResult = actionResult as BadRequestResult;
// Assert
badRequestResult.Should().NotBeNull();
badRequestResult?.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
}
所以,我的控制器中有一个创建方法:
[Authorize]
[Route("[controller]")]
[ApiController]
public class ConversionsController : ControllerBase
{
private readonly IGenericService<Conversion> _conversionService;
public ConversionsController(IGenericService<Conversion> conversionService) => _conversionService = conversionService;
/// <summary>
/// Creates a new conversion
/// </summary>
/// <param name="conversion">The conversion</param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(Conversion), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync(ConversionViewModel conversion)
{
if (conversion == null) return BadRequest();
if (!ModelState.IsValid) return BadRequest(ModelState);
var request = ModelFactory.Create(conversion);
_conversionService.Create(request);
await _conversionService.SaveChangesAsync();
return Created(nameof(Get), new Sxp.Web.ActionResult<Conversion>(request, string.Format(Resources.EntityCreated, "conversion")));
}
}
如您所见,如果没有模型,它将 return 一个错误的请求(有效)。
但是下一行说明 if (!ModelState.IsValid)
永远不会起作用。它始终 return 有效,即使在 null 处需要 属性。
我写了一个测试,总是失败:
[Test]
public async Task ReturnBadRequestIfNullRequiredProperty()
{
// Assemble
var services = ConversionsControllerContext.GivenServices();
var controller = services.WhenCreateController();
// Act
var actionResult = await controller.CreateAsync(new ConversionViewModel());
var badRequestResult = actionResult as BadRequestResult;
// Assert
badRequestResult.Should().NotBeNull();
badRequestResult?.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
}
正如您在这里看到的,我只是传递了一个 CategoryViewModel
,根本没有设置任何属性,但它失败了。
视图模型如下所示:
public class ConversionViewModel
{
public int Id { get; set; }
[Range(1, int.MaxValue, ErrorMessageResourceName = "RangeErrorMessage", ErrorMessageResourceType = typeof(Resources))] public int FeedId { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string Name { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string FieldName { get; set; }
[Required(ErrorMessageResourceName = "RequiredErrorMessage", ErrorMessageResourceType = typeof(Resources)), StringLength(100, ErrorMessageResourceName = "StringLengthErrorMessage", ErrorMessageResourceType = typeof(Resources))] public string Expression { get; set; }
public double Value { get; set; }
public MathOperator MathOperator { get; set; }
public FilterOperator FilterOperator { get; set; }
}
我唯一能想到的是我禁用了自动状态验证:
.ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; });
我认为这让我可以在控制器中处理它。
有人遇到过这个吗?
如果您使用 services.AddMvcCore
而不是 services.AddMvc
然后添加
services.AddDataAnnotations();
到Startup.ConfigureServices方法
您正在 运行 进行单元测试,而不是端到端集成测试,因此在进行测试时,某些框架问题不会 运行 进行。
这些数据注释属性是仅在 运行 时被框架识别的元数据,而不是在隔离单元测试期间识别的元数据,因为当应用程序处于 运行 时它们实际上被模型绑定器读取宁.
如果目的是让框架验证模型,则需要进行集成测试。
引用Integration tests in ASP.NET Core
否则,被测对象(控制器)必须在安排测试时手动更新其 ModelState
,以便在执行时表现出预期的效果。
通过使用 AddModelError
添加错误来测试无效的模型状态,如下面的测试所示:
[Test]
public async Task ReturnBadRequestIfNullRequiredProperty() {
// Arrange / Assemble
var services = ConversionsControllerContext.GivenServices();
var controller = services.WhenCreateController();
controller.ModelState.AddModelError("Name","Name required"); //<-- Invalidate model state
//...other desired errors.
// Act
var actionResult = await controller.CreateAsync(new ConversionViewModel());
var badRequestResult = actionResult as BadRequestResult;
// Assert
badRequestResult.Should().NotBeNull();
badRequestResult?.StatusCode.Should().Be(StatusCodes.Status400BadRequest);
}