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)
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)
基于@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中完成 - 请编辑此帖或评论.