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

C# 使用Web API对接口属性进行模型绑定,c#,asp.net-web-api,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 Use

我有一个命令,看起来像:

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; }
}
其余请求是:

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"
}
我有一个API控制器动作,看起来:

[HttpPut, Route("create")]
public IHttpActionResult CreateSomething([FromBody]CreateSomething command)
{
    // I would like command.UserId already binded here
}
我的模型上的
Title
属性由请求主体填写,但我想使用请求头中的一些值(例如,来自身份验证令牌)绑定
command.UserId
属性

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

我在Web API中尝试了
IModelBinder
接口的各种组合,但没有真正的运气

使用它也感觉多余:

[HttpPut, Route("create")]
public IHttpActionResult CreateSomething([FromBody]CreateSomething command)
{
    command.UserId = GetUserIdFromTheRequest();
}
或者从控制器的依赖项中获取
用户ID
,并按上述方式进行设置

如何在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;
    }
}
并在启动时将其连接到:

ModelBinders.Binders.DefaultBinder = new UserAwareModelBinder();
并在类型声明上添加属性

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

根据@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!
        }
    }
}
然后将其连接到
HttpConfiguration

configuration.ParameterBindingRules.Insert(0, descriptor => typeof(IUserAware).IsAssignableFrom(descriptor.ParameterType) ? new UserAwareHttpParameterBinding(descriptor) : null);
如果有人知道这是如何在.NET核心MVC中完成的,请编辑此文件 张贴或评论


感谢您的建议,是否可以创建一种更通用的处理方法?我希望避免手动绑定其他属性-我只希望绑定基于接口的属性-其他属性应按常规绑定。ModelBinder用于绑定到特定的自定义对象。对于用户身份验证,您应实现web api安全性,请阅读并使用HttpContext.Current.user或创建基本控制器可以实现CurrentUser并将用户从模型绑定过程中分离出来。不仅需要身份验证,还需要一个关于如何在WebAPI中绑定自定义属性的一般问题。我已经更新了关于如何在MVC中执行此操作的问题,我只是不知道如何单独在WebAPI中执行。MVC中自定义属性的绑定就像您编写的:
public IHttpActionResult CreateSomething([FromBody]CreateSomething命令)
但是您不能期望MVC猜测从何处获取对象的值。在您的情况下,您应该在正文中传递带有标题的userId。不,不是。覆盖了
BindModel
的DefaultModelBinder是MVC名称空间的东西:System.Web.MVC。
configuration.ParameterBindingRules.Insert(0, descriptor => typeof(IUserAware).IsAssignableFrom(descriptor.ParameterType) ? new UserAwareHttpParameterBinding(descriptor) : null);