Proprietà dell’interfaccia modello-binding con Web API

Ho un comando simile a:

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

La richiesta REST è:

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

Ho un’azione di controller API alla ricerca di:

 [HttpPut, Route("create")] public IHttpActionResult CreateSomething([FromBody]CreateSomething command) { // I would like command.UserId already binded here } 

La proprietà Title sul mio modello è compilata con il corpo della richiesta, ma vorrei associare la proprietà command.UserId utilizzando alcuni valori dalle intestazioni delle richieste (ad esempio da un token di autenticazione).

Come posso associare la proprietà di IUserAware a un valore di intestazione della richiesta, ad esempio un modello-legante, senza dover creare un raccoglitore per la class concreta CreateSomething ?

Ho provato varie combinazioni dell’interfaccia di IModelBinder in Web API, ma senza alcuna fortuna.

Si sente anche ridondante da usare:

 [HttpPut, Route("create")] public IHttpActionResult CreateSomething([FromBody]CreateSomething command) { command.UserId = GetUserIdFromTheRequest(); } 

O ottenendo l’ UserId da una dipendenza sul controller e impostarlo come sopra.

Come è fatto in ASP.NET MVC

In ASP.NET MVC è ansible fare quanto segue per farlo funzionare:

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

E collegalo all’avvio con:

 ModelBinders.Binders.DefaultBinder = new UserAwareModelBinder(); 

 public class CreateSomethingModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { string key = bindingContext.ModelName; ValueProviderResult val = bindingContext.ValueProvider.GetValue(key); if (val != null) { string s = val.AttemptedValue as string; if (s != null) { return new CreateSomething(){Title = s; UserId = new Guid(ControllerContext.HttpContext.Request.Headers["userId"]);} } } return null; } } 

e aggiungi un attributo nella dichiarazione del tipo

 [ModelBinder(typeof(CreateSomethingModelBinder))] public class CreateSomething { ... } 

Sulla base dell’ultimo commento di @Todd, la risposta alla domanda è:

Creare una class HttpParameterBinding :

 public class UserAwareHttpParameterBinding : HttpParameterBinding { private readonly HttpParameterBinding _paramaterBinding; private readonly HttpParameterDescriptor _httpParameterDescriptor; public UserAwareHttpParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor) { _httpParameterDescriptor = descriptor; _paramaterBinding = new FromBodyAttribute().GetBinding(descriptor); } public override async Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { await _paramaterBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken); var baseModel = actionContext.ActionArguments[_httpParameterDescriptor.ParameterName] as IUserAware; if (baseModel != null) { baseModel.UserId = new Guid("6ed85eb7-e55b-4049-a5de-d977003e020f"); // Or get it form the actionContext.RequestContext! } } } 

E HttpConfiguration in HttpConfiguration :

 configuration.ParameterBindingRules.Insert(0, descriptor => typeof(IUserAware).IsAssignableFrom(descriptor.ParameterType) ? new UserAwareHttpParameterBinding(descriptor) : null); 

Se qualcuno sa come è fatto in .NET Core MVC, modifica questo post o commento.