swd*_*don 5 c# model-binding custom-model-binder asp.net-core
任何人都有使用多态模型绑定的自定义模型绑定的工作示例?我正在尝试使用 web api 项目的这个示例(用于 Mvc 而不是 Api 项目),但它不适用于 API 项目。我认为在填充方面缺少一些步骤,ValueProvider
但我找不到与此相关的任何资源(AspNet Core 3.1)。
到目前为止我的尝试:
托斯:
public abstract class Device
{
public string Kind { get; set; }
}
public class Laptop : Device
{
public string CPUIndex { get; set; }
}
public class SmartPhone : Device
{
public string ScreenSize { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
自定义模型绑定器实现:
public class DeviceModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Device))
{
return null;
}
var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };
var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
foreach (var type in subclasses)
{
var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
}
return new DeviceModelBinder(binders);
}
}
public class DeviceModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Kind));
var modelTypeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue.FirstValue == "Laptop")
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (modelTypeValue.FirstValue == "SmartPhone")
{
(modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
bindingContext.ActionContext,
bindingContext.ValueProvider,
modelMetadata,
bindingInfo: null,
bindingContext.ModelName);
await modelBinder.BindModelAsync(newBindingContext);
bindingContext.Result = newBindingContext.Result;
if (newBindingContext.Result.IsModelSet)
{
// Setting the ValidationState ensures properties on derived types are correctly
bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
Run Code Online (Sandbox Code Playgroud)
我像这样注册模型绑定器提供者:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(o => o.ModelBinderProviders.Insert(0, new DeviceModelBinderProvider()));
}
Run Code Online (Sandbox Code Playgroud)
然后我的控制器:
[ApiController]
[Route("test")]
public class TestController : ControllerBase
{
[HttpPost]
public IActionResult Test(Device dto)
{
var x = dto;
return Ok();
}
}
Run Code Online (Sandbox Code Playgroud)
我发布了一个 json 请求正文,如:
{
"ScreenSize": "1",
"Kind": "SmartPhone"
}
Run Code Online (Sandbox Code Playgroud)
真的厌倦了这方面的文档,因为有太多的魔法在发生。我的回退是从请求中手动解析 HttpContent 并反序列化。但我希望像示例中那样使用模型绑定器方法。我看到的唯一两件奇怪的事情是,bindingContext.ModelName
是空的,并且bindingContext.ValueProvider
只有一个包含action
和controller
键的路由值提供程序。所以,看起来身体甚至没有被解析为值提供者。
我已经尝试过与您发布的完全相同的代码并且它对我有用。
这是其价值的形象。
这是邮递员请求的屏幕截图。
来自邮递员的 CURL 请求。
curl --location --request POST 'https://localhost:44332/test' \
--header 'Content-Type: application/json' \
--form 'ScreenSize=1' \
--form 'Kind=SmartPhone'
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2570 次 |
最近记录: |