Access route data in FluentValidation for WebApi 2

2019-07-19 08:19发布

I have a basic C# Web Api 2 controller that has a POST method to create an entity

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

And also a PUT method to update said model

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

And here is the UserModel

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

For my validator, I want to validate that the name is not taken on Post - easy enough. For PUT, I need to validate that the name is not taken, by another user, but of course this particular user would have the same username.

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;
    }
}

I am using AutoFac, so maybe there is a way to inject the HttpRequest into the validator and get the route data that way.

Or possibly I could create a model binder that looks for the route data and adds it to the model?

Or is there an easy way?

1条回答
孤傲高冷的网名
2楼-- · 2019-07-19 08:49

The easiest way of course is to add the Id to the UserModel. You'd have to add some extra checking on the Post and Put operations though. The first should ignore the Id when a client provides it. The second could check whether the Id in the path is the same as the Id in the model. If not, then return a BadRequest.

Altered model:

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

Altered methods:

public HttpResponseMessage Post(UserModel userModel)
{
   // ignore provided userModel.Id
}

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

Update

Having an Id in the route as well as in the model does indeed allow for a discrepancy between the two as you commented. A respectful API consumer will probably not post a request with misaligned Ids. But a malicious consumer (aka hacker) most probably will. Therefore you should return BadRequest when the Ids don't match.

You certainly do not want to update the UserModel with the Id as you mentioned otherwise you might end up with user 1 (the one in the url) overwritten by the details of user 2 (the one in the UserModel).

查看更多
登录 后发表回答