经过大量的谷歌搜索,我能找到并解决我的问题的唯一解决方案是创建我自己的活页夹来模仿默认的BodyModelBinder。
然后,在InputFormatterContext 被实例化的地方,我没有将bindingContext.ModelMetadata 作为第四个参数传递,而是传递bindingContext.ModelMetadata.GetMetadataForType([desired type here]),其中我传递的类型取决于其他上下文值(例如路由参数、控制器和动作上下文等)。
然后我像这样在我的操作中使用 binder 属性来使用 binder
[HttpPut("{resource}"), ActionName("Create")]
public async Task<IActionResult> CreateResource(
[ModelBinder(BinderType = typeof(BodyResourceModelBinder))] object model
)
{
...
}
通过在模型实例中执行.GetType(),我得到了我在活页夹代码中设置的实际类型。
最终的活页夹代码是这样的:
public class BodyResourceModelBinder: IModelBinder
{
private readonly IList<IInputFormatter> _formatters;
private readonly Func<Stream, Encoding, TextReader> _readerFactory = (s, e) => new StreamReader(s, e);
private readonly ILogger _logger;
public BodyResourceModelBinder(IOptions<MvcOptions> mvcOptions, ILogger<BodyResourceModelBinder> logger = null)
{
if(mvcOptions == null || mvcOptions.Value == null)
throw new ArgumentNullException(nameof(mvcOptions));
_formatters = mvcOptions.Value.InputFormatters.ToList();
_logger = logger;
}
internal bool AllowEmptyBody { get; set; }
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if(bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
string modelBindingKey;
if(bindingContext.IsTopLevelObject)
{
modelBindingKey = bindingContext.BinderModelName ?? string.Empty;
}
else
{
modelBindingKey = bindingContext.ModelName;
}
var httpContext = bindingContext.HttpContext;
var formatterContext = new InputFormatterContext(
httpContext,
modelBindingKey,
bindingContext.ModelState,
// THIS IS THE ACTUAL CHANGE. I CREATE NEW METADATA BASED ON THE TYPE I WANT TO BIND TO
bindingContext.ModelMetadata.GetMetadataForType(typeof(<PUT YOUR TYPE HERE>)),
_readerFactory,
AllowEmptyBody);
var formatter = (IInputFormatter)null;
for(var i = 0; i < _formatters.Count; i++)
{
if(_formatters[i].CanRead(formatterContext))
{
formatter = _formatters[i];
break;
}
}
if(formatter == null)
{
var message = $"Unsupported content type: {httpContext.Request.ContentType}";
var exception = new UnsupportedContentTypeException(message);
bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);
return;
}
try
{
var result = await formatter.ReadAsync(formatterContext);
if(result.HasError)
return;
if(result.IsModelSet)
{
// The actual type of result.Model here is the type you provided in the Metadata of the IInputFormatter above
var model = result.Model;
bindingContext.Result = ModelBindingResult.Success(model);
}
else
{
var message = bindingContext
.ModelMetadata
.ModelBindingMessageProvider
.MissingRequestBodyRequiredValueAccessor();
bindingContext.ModelState.AddModelError(modelBindingKey, message);
}
}
catch(Exception exception) when(exception is InputFormatterException || ShouldHandleException(formatter))
{
bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);
}
}
private bool ShouldHandleException(IInputFormatter formatter)
{
// Any explicit policy on the formatters overrides the default.
var policy = (formatter as IInputFormatterExceptionPolicy)?.ExceptionPolicy ??
InputFormatterExceptionPolicy.MalformedInputExceptions;
return policy == InputFormatterExceptionPolicy.AllExceptions;
}
}