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?
The easiest way of course is to add the
Id
to theUserModel
. You'd have to add some extra checking on thePost
andPut
operations though. The first should ignore theId
when a client provides it. The second could check whether theId
in the path is the same as theId
in the model. If not, then return aBadRequest
.Altered model:
Altered methods:
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 theUserModel
).