I have a Web API 2 project and an other project, in which I have my Model classes and a BaseModel that is a base for all Models, as following,
public class BaseModel
{
public string UserId { get; set; }
}
All the other models are derived from my BaseModel.
In Web API I have my CustomerController as following,
public class CustomerController : ApiController
{
[HttpPost]
public GetCustomerResponseModel Get(GetCustomerRequestModel requestModel)
{
var response = new GetCustomerResponseModel();
//I need only the UserId coming from the BaseModel is binded from request headers
var userId = requestModel.UserId;
//I want all model data except UserId is binded with default model binding
var customerData = requestModel.CustomerData;
var someOtherData = requestModel.SomeOtherData;
return response;
}
[HttpPost]
public AddStockAlertResponseModel AddStockAlert(AddStockAlertRequestModel requestModel)
{
var response = new AddStockAlertResponseModel();
//I need only the UserId coming from the BaseModel is binded from request headers
var userId = requestModel.UserId;
//I want all model data except UserId is binded with default model binding
var stockInfo = requestModel.StockInfo;
return response;
}
}
Every request that comes to CustomerController has a "UserId" header in request headers and I need a ModelBinder or ParameterBinder or some functionality that binds only the UserId from request headers without touching the other model parameters. I mean model parameters except UserId are to be bound by default.
I don't want to use AOP or interceptors or aspects. Is it possible to bind only UserId with ASP.NET functionality like model binders, parameter binders, etc.?
Following is a quick example using HttpParameterBinding
. Here I am creating a custom parameter binding where I let the default FromBody
based binding to use the formatters to deserialize the request body and then I get the user id from request headers and set on the the deserialized object. (You might need to add additional validation checks on the following code).
config.ParameterBindingRules.Insert(0, (paramDesc) =>
{
if (typeof(BaseModel).IsAssignableFrom(paramDesc.ParameterType))
{
return new BaseModelParamBinding(paramDesc);
}
// any other types, let the default parameter binding handle
return null;
});
public class BaseModelParamBinding : HttpParameterBinding
{
HttpParameterBinding _defaultFromBodyBinding;
HttpParameterDescriptor _paramDesc;
public BaseModelParamBinding(HttpParameterDescriptor paramDesc)
: base(paramDesc)
{
_paramDesc = paramDesc;
_defaultFromBodyBinding = new FromBodyAttribute().GetBinding(paramDesc);
}
public override async Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, CancellationToken cancellationToken)
{
await _defaultFromBodyBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken);
BaseModel baseModel = actionContext.ActionArguments[_paramDesc.ParameterName] as BaseModel;
if (baseModel != null)
{
baseModel.UserId = actionContext.Request.Headers.GetValues("UserId").First();
}
}
}