【问题标题】:Return BadRequest with null required property in model .net core web api在模型 .net 核心 Web api 中返回带有 null 必需属性的 BadRequest
【发布时间】:2020-02-20 14:29:07
【问题描述】:

所以,我的控制器中有一个 create 方法:

[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")));
    }
}

如您所见,如果没有模型,它将返回错误请求(有效)。

但是下一行声明if (!ModelState.IsValid) 永远不会起作用。即使在 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; });

我认为这可以让我在控制器中处理它。

以前有人遇到过这种情况吗?

【问题讨论】:

    标签: c# testing asp.net-core asp.net-core-webapi modelstate


    【解决方案1】:

    您正在运行单元测试,而不是端到端集成测试,因此在执行测试时某些框架问题未运行。

    这些数据注释属性是元数据,仅在运行时由框架识别,而不是在隔离单元测试期间,因为它们实际上是在应用程序运行时由模型绑定器读取的。

    如果打算让框架验证模型,则需要进行集成测试。

    参考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);
    }
    

    参考Test controller logic in ASP.NET Core

    【讨论】:

      【解决方案2】:

      如果您使用的是services.AddMvcCore 而不是services.AddMvc,请添加

      services.AddDataAnnotations();
      

      到 Startup.ConfigureServices 方法

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 2019-04-08
        • 2018-04-07
        • 2019-08-28
        • 2015-02-10
        相关资源
        最近更新 更多