Question

I have a webApi2 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 webapi 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 binded by default..

I don't want to use AOP or interceptors or aspects.. Is it possible to bind only UserId with an asp.net functionality like model binders, parameter binders, etc.

Was it helpful?

Solution

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();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top