【问题标题】:Access route data in FluentValidation for WebApi 2在 FluentValidation for WebApi 2 中访问路由数据
【发布时间】:2016-03-03 20:22:17
【问题描述】:

我有一个基本的 C# Web Api 2 控制器,它有一个 POST 方法来创建实体

public HttpResponseMessage Post(UserModel userModel){ ... }

还有一个 PUT 方法来更新所述模型

public HttpResponseMessage Put(int id, UserModel userModel) { ... }

这里是用户模型

public class UserModel
{
    public virtual Name { get; set; }
    public virtual Username { get; set; }
}

对于我的验证器,我想验证该名称未在 Post 上使用 - 很简单。对于 PUT,我需要验证该名称是否未被其他用户使用,但当然该特定用户将具有相同的用户名。

public class UserModelValidator : AbstractValidator<UserModel>
{
    public UserModelValidator()
    {
        RuleFor(user => user.Username)
            .Must(NotDuplicateName).WithMessage("The username is taken");

    }

    private bool NotDuplicateName(string username)
    {
        var isValid = false;

        //Access repository and check to see if username is not in use
        //If it is in use by this user, then it is ok - but user ID is
        //in the route parameter and not in the model.  How do I access?

        return isValid;
    }
}

我正在使用 AutoFac,所以也许有一种方法可以将 HttpRequest 注入验证器并以这种方式获取路由数据。

或者我可以创建一个模型绑定器来查找路线数据并将其添加到模型中?

或者有什么简单的方法吗?

【问题讨论】:

    标签: c# asp.net-web-api fluentvalidation


    【解决方案1】:

    我找到了另一种解决方案,将IActionContextAccessor 注入验证器。有了这个,我可以访问 ROUTE 参数而不需要特殊的模型绑定。

    Startup.cs

    services.AddHttpContextAccessor();
    services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
    

    UserModelValidator.cs

    public class UserModelValidator : AbstractValidator<UserModel>
    {
        public UserModelValidator(IActionContextAccessor actionContextAccessor)
        {
            RuleFor(item => item.Username)
                .MustAsync(async (context, username, propertyValidatorContext, cancellationToken) =>
                {
                    var userId = (string)actionContextAccessor.ActionContext.RouteData.Values
                    .Where(o => o.Key == "userId")
                    .Select(o => o.Value)
                    .FirstOrDefault();
    
                    return true;
                });
        }
    }
    

    【讨论】:

      【解决方案2】:

      最简单的方法当然是将Id 添加到UserModel。不过,您必须对 PostPut 操作添加一些额外的检查。当客户提供Id 时,第一个应该忽略它。第二个可以检查路径中的Id是否与模型中的Id相同。如果没有,则返回BadRequest

      改变的模型:

      public class UserModel
      {
          public virtual Id { get; set; }
          public virtual Name { get; set; }
          public virtual Username { get; set; }
      }
      

      改变方法:

      public HttpResponseMessage Post(UserModel userModel)
      {
         // ignore provided userModel.Id
      }
      
      public HttpResponseMessage Put(int id, UserModel userModel)
      {
         if(id != userModel.Id)
         {
             // return bad request response
         }
      }
      

      更新

      正如您评论的那样,在路线和模型中都有一个 Id 确实允许两者之间存在差异。尊重的 API 使用者可能不会发布带有未对齐 Id 的请求。但是恶意消费者(又名黑客)很可能会这样做。因此,当 Id 不匹配时,您应该返回 BadRequest

      您当然不想使用您提到的 Id 更新 UserModel,否则您最终可能会被用户 2 的详细信息(UserModel 中的那个)覆盖用户 1(url 中的那个) )。

      【讨论】:

      • 感谢您的回复,但这会导致资源(URL)和模型之间存在差异。此外,在 FluentValidators 触发时,它们只能访问模型。我确实最终将 Id 添加到了 UserModel,但还添加了一个 ActionFilter,它将 UserModel 中的 Id 更新为通过 URL 传入的 id。
      • 我在回复您的评论时添加了解释。
      • 谢谢 - 是的,你可能是对的,它应该返回 BadRequest - 模型中的 Id 不是 put 请求的有效参数(从客户端的角度来看),并且仅在后面使用 -结束,但返回一个 BadRequest 可能是有意义的。
      • @Shibbz 您能否展示一下您是如何创建该操作过滤器的?我正在尝试做同样的事情(访问路线数据),您的建议可能对我有用
      • @Misiu 当然,here's a gist。代码可能会被清理
      猜你喜欢
      • 2016-02-03
      • 2017-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-26
      • 1970-01-01
      • 2018-12-28
      相关资源
      最近更新 更多