I have a command looking like:
public interface ICommand {
// Just a marker interface
}
public interface IUserAware {
Guid UserId { get; set; }
}
public class CreateSomething : ICommand, IUserAware
{
public string Title { get; set; }
public Guid UserId { get; set; }
}
The REST request is:
PUT /create HTTP/1.1
UserId: 7da6f9ee-2bfc-70b1-f93c-10c950c8f6b0 // Possible an Auth token and not a userId like here.
Host: localhost:63079
Content-Type: application/json
Cache-Control: no-cache
{
"title": "This is a test title"
}
I have a API controller action looking:
[HttpPut, Route("create")]
public IHttpActionResult CreateSomething([FromBody]CreateSomething command)
{
// I would like command.UserId already binded here
}
The Title
property on my model is filled out with the body of the request, but I would like to bind the command.UserId
property using some values from the request headers (e.g. from a authentication token).
How can I bind the property of IUserAware
from a request header value, with e.g. a model-binder, without having to create a binder for the concrete class CreateSomething
?
I've tried various combinations of the IModelBinder
interface in Web API, but with no real luck.
It also feels redundant to to use:
[HttpPut, Route("create")]
public IHttpActionResult CreateSomething([FromBody]CreateSomething command)
{
command.UserId = GetUserIdFromTheRequest();
}
Or getting the UserId
from a dependency on the controller and set it like the above.
How it is done in ASP.NET MVC
In ASP.NET MVC it is possible to do the following to get it work:
public class UserAwareModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
{
var baseModel = base.CreateModel(controllerContext, bindingContext, modelType);
var commandModel = baseModel as IUserAware;
if (commandModel != null)
{
commandModel.UserId = controllerContext.HttpContext.User; // or get it from the HttpContext headers.
}
return baseModel;
}
}
And wire it up at startup with:
ModelBinders.Binders.DefaultBinder = new UserAwareModelBinder();