【问题标题】:EF Core Web API: foreign key validationEF Core Web API:外键验证
【发布时间】:2021-03-30 13:39:45
【问题描述】:

假设我有这些课程:

public class User
{
    public int? Id { get; set; }
    ...
}

public class Wallet
{
    public int? Id { get; set; }
    public string Title {get; set;}
    public User Owner { get; set; }
    ...
}

public class WalletCreationDTO 
{
    [Required]
    public string Title {get; set;}
    [Required]
    public int OwnerId { get; set; }
}

[HttpPost]
public async Task<ActionResult> CreateWallet(WalletCreationDto walletDTO) {...}

我必须像这样向前端报告任何错误的参数:

{  
    "errors": {  
        "ownerId": "User by Id does not exist",  
        "title": "Must not exceed 8 characters"  
    }  
}

如何验证 OwnerId?

  • 手动查询数据库似乎无效
  • 默认的 ASP.NET Core 验证似乎没有任何与数据库访问相关的注释

【问题讨论】:

  • 我建议您尝试 FluentValidation。对于服务器端验证,我发现它非常有用。请注意,这不是广告。
  • 看起来不错,但在控制器中包含流畅的验证似乎很麻烦,因为我正在使用 dabatase 上下文进行验证
  • 您可以创建一个自定义验证器(实现 IValidation)和retrieve the context 进行检查
  • 看起来很乱

标签: c# database validation asp.net-core entity-framework-core


【解决方案1】:

我最终使用了流畅的验证as suggested。这不是最有效的方法,但我正在权衡漂亮的错误和简单的代码。

public class WalletCreateDTO
{
    [Required]
    [MaxLength(20)]
    public string Title { get; set; }

    [Required]
    public int OwnerId { get; set; }

    [Required]
    public string CurrencyCode { get; set; }
}

public class WalletCreateDTOValidator : AbstractValidator<WalletCreateDTO>
{
    public WalletCreateDTOValidator(Data.Database database)
    {
        this.RuleFor(w => w.OwnerId)
            .Must(ownerId => database.Users.Any(user => user.Id == ownerId))
            .WithMessage("User does not exist");
        this.RuleFor(w=> w.CurrencyCode)
            .Must(currencyCode => database.Currencies.Any(currency => currency.Code == currencyCode))
            .WithMessage("Currency does not exist");
    }
}

--- To anyone reading this in the future ---

NuGet 包:FluentValidation.AspNetCore

配置:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddFluentValidation(config =>
        {
            config.RegisterValidatorsFromAssembly(typeof(WalletCreateDTO).Assembly);
        });
}

注意:这似乎注册了程序集中的所有验证器。比如我所有的DTO都在Api.Dtos目录/命名空间里,好像都注册了,很方便。

此外,还有大摇大摆的集成:MicroElements.Swashbuckle.FluentValidation

【讨论】:

    【解决方案2】:

    您可以在简单模式下使用它。

            [HttpPost]
            public async Task<ActionResult> CreateWallet(WalletCreationDto walletDTO)
            {
                bool isValidOwnerId = /*validationLogic => can use service or Repository or etc... */
    
                if (isValidOwnerId == false)
                    ModelState.AddModelError("OwnerId", "errorMessage");
    
    
                if (ModelState.IsValid)
                {
                    //CreateWallet Loigc......
                }
    
                //we can get all errors from ModelState
                List<ModelError> allErrors = ModelState.Values.SelectMany(v => v.Errors).ToList();
    
                /*
                 return .... 
                */
            }
    

    我还建议您阅读以下两个链接。

    IValidatableObject

    fluentvalidation:用于构建强类型验证规则的流行 .NET 库。

    【讨论】:

    • 看起来不错,尽管我需要对仅用于验证一个属性的其他服务的引用。似乎真的没有办法利用数据库自​​己的输入验证方式
    • 如果你的意思是“数据库自己的方式”,这是=>_context.Users.Any(e =&gt; e.Id == OwnerId);,你可以使用相同的方法(_context类型可以是DbContext或IdentityDbContext)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    • 1970-01-01
    • 2016-12-22
    • 2019-03-26
    • 1970-01-01
    • 2021-11-14
    相关资源
    最近更新 更多