使用Web API进行模型绑定接口属性

jan*_*ann 16 c# asp.net-web-api

我有一个命令看起来像:

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; }
}
Run Code Online (Sandbox Code Playgroud)

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"
}
Run Code Online (Sandbox Code Playgroud)

我有一个API控制器动作:

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

Title我的模型上的属性用请求的主体填充,但我想command.UserId使用请求头中的一些值(例如来自身份验证令牌)绑定属性.

如何绑定IUserAware请求标头值的属性,例如模型绑定器,而不必为具体类创建绑定器CreateSomething

IModelBinder在Web API中尝试了各种界面组合,但没有真正的运气.

使用时也感觉多余:

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

或者UserId从控制器获取依赖关系并像上面那样设置它.

它是如何在ASP.NET MVC中完成的

在ASP.NET MVC中,可以执行以下操作以使其工作:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并在启动时将其连接起来:

ModelBinders.Binders.DefaultBinder = new UserAwareModelBinder();
Run Code Online (Sandbox Code Playgroud)

Tod*_*odd 6

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并在类型声明上添加属性

[ModelBinder(typeof(CreateSomethingModelBinder))]
public class CreateSomething  { ... }
Run Code Online (Sandbox Code Playgroud)


jan*_*ann 6

基于@Todd的最后评论,该问题的答案是:

创建一个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!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并将其连接到HttpConfiguration:

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

如果有人知道如何在.NET Core MVC中完成 - 请编辑此帖或评论.