FluentValidator和JsonPatchDocument

时间:2019-05-28 14:18:06

标签: api asp.net-core-2.0 patch fluentvalidation json-patch

我具有WebAPI(.NET Core),并使用FluentValidator验证模型,包括更新。 我使用PATCH动词,并具有以下方法:

    public IActionResult Update(int id, [FromBody] JsonPatchDocument<TollUpdateAPI> jsonPatchDocument)
    {

另外,我有一个验证器类:

public class TollUpdateFluentValidator : AbstractValidator<TollUpdateAPI>
{
    public TollUpdateFluentValidator ()
    {
        RuleFor(d => d.Date)
            .NotNull().WithMessage("Date is required");

        RuleFor(d => d.DriverId)
            .GreaterThan(0).WithMessage("Invalid DriverId");

        RuleFor(d => d.Amount)
            .NotNull().WithMessage("Amount is required");

        RuleFor(d => d.Amount)
            .GreaterThanOrEqualTo(0).WithMessage("Invalid Amount");
    }
}

并在启动类中映射此验证器:

        services.AddTransient<IValidator<TollUpdateAPI>, TollUpdateFluentValidator>();

但不起作用。如何为我的任务编写有效的FluentValidator?

3 个答案:

答案 0 :(得分:0)

您将需要手动触发验证。 您的操作方法将如下所示:

public IActionResult Update(int id, [FromBody] JsonPatchDocument<TollUpdateAPI> jsonPatchDocument)
{
    // Load your db entity
    var myDbEntity = myService.LoadEntityFromDb(id);

    // Copy/Map data to the entity to patch using AutoMapper for example
    var entityToPatch = myMapper.Map<TollUpdateAPI>(myDbEntity);

    // Apply the patch to the entity to patch
    jsonPatchDocument.ApplyTo(entityToPatch);

    // Trigger validation manually
    var validationResult = new TollUpdateFluentValidator().Validate(entityToPatch);
    if (!validationResult.IsValid)
    {
        // Add validation errors to ModelState
        foreach (var error in validationResult.Errors)
        {
            ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
        }

        // Patch failed, return 422 result
        return UnprocessableEntity(ModelState);
    }

    // Map the patch to the dbEntity
    myMapper.Map(entityToPatch, myDbEntity);
    myService.SaveChangesToDb();

    // So far so good, patch done
    return NoContent();
}

答案 1 :(得分:0)

您可以为此使用自定义规则构建器。它可能不是最优雅的处理方式,但至少验证逻辑是您期望的位置。

假设您具有以下请求模型:

public class CarRequestModel
{
    public string Make { get; set; }
    public string Model { get; set; }
    public decimal EngineDisplacement { get; set; }
}

您的Validator类可以继承自AbstractValidator的{​​{1}},而不是具体的请求模型类型。

另一方面,流利的验证器为我们提供了不错的扩展点,例如“自定义”规则。

结合这两个想法,您可以创建如下内容:

JsonPatchDocument

在某些情况下,写下从域的角度来看不允许但在enter image description here中定义的所有动作可能很繁琐。

可以通过定义 none但是规则来缓解此问题,这些规则将从您的域的角度定义有效的一组操作。

答案 2 :(得分:0)

下面的实现允许在 IValidator<Model> 中使用 IValidator<JsonPatchDocument<Model>>,但您需要创建具有有效属性值的模型。

public class ModelValidator : AbstractValidator<JsonPatchDocument<Model>>
{
    public override ValidationResult Validate(ValidationContext<JsonPatchDocument<Model>> context)
    {
        return _validator.Validate(GetRequestToValidate(context));
    }

    public override Task<ValidationResult> ValidateAsync(ValidationContext<JsonPatchDocument<Model>> context, CancellationToken cancellation = default)
    {
        return _validator.ValidateAsync(GetRequestToValidate(context), cancellation);
    }

    private static Model GetRequestToValidate(ValidationContext<JsonPatchDocument<Model>> context)
    {
        var validModel = new Model()
                           {
                               Name = nameof(Model.Name),
                               Url = nameof(Model.Url)
                           };

        context.InstanceToValidate.ApplyTo(validModel);
        return validModel;
    }

    private class Validator : AbstractValidator<Model>
    {
        /// <inheritdoc />
        public Validator()
        {
            RuleFor(r => r.Name).NotEmpty();
            RuleFor(r => r.Url).NotEmpty();
        }
    }

    private static readonly Validator _validator = new();
}