我有一个Angular客户端,并使用此主体创建POST请求:
{“名称”:“示例”,“货币”:“ EUR”}
我使用Odata协议,我的控制器是:
[HttpPost, ODataRoute("Templates")]
public IActionResult Insert([FromBody] Template value)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
value.Id = Guid.NewGuid();
_context.Templates.Add(value);
_context.SaveChanges();
return Created(value);
}
含模板:
public class Template
{
public Guid Id { get; set; }
public string Name { get; set; }
public Currency Currency { get; set; }
}
和货币:
[Serializable]
public class Currency : StringEnumeration<Currency>
{
public static Currency EUR = new Currency("EUR", "EUR");
public static Currency USD = new Currency("USD", "USD");
Currency() { }
Currency(string code, string description) : base(code, description) { }
}
货币是一个特殊的类,因为它具有私有的构造函数,因此,我无法创建Currency的新实例。我想使用现有实例之一(欧元或美元)。
(StringEnumeration支持Parse和TryParse方法并返回正确的实例)
标准配置:
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureCors();
services.AddOData();
services.ConfigureIISIntegration();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<GpContext>(option => option
.UseSqlServer(Configuration.GetConnectionString(GpConnection)));
}
我的问题是客户端在http://localhost:4200/template上调用POST 正文:{“ Name”:“ example”,“ Currency”:“ EUR”}
模型Bindel无法在Currency.EUR实例中转换“ EUR”,因此我想提供一些帮助模型绑定器使用实例Currency.EUR创建具有Currency属性的模板
这是生成的错误: 尝试读取属性“ Currency”的值时,发现一个非空值的“ PrimitiveValue”节点;但是,应该使用具有空值的'StartArray'节点,'StartObject'节点或'PrimitiveValue'节点。
在我的项目中,我有很多带有Currency属性的类。
我尝试在Template类上使用IModelBinder,并且它可以工作,但是我不想为任何Currency属性编写一个modelBinder。
我尝试过使用JsonConverter,但是它对我不起作用(可能有问题)
我的预期结果是具有以下值的Template实例:
Id = defaluf(Guid)
Name = "example"
Currency = Currency.EUR
答案 0 :(得分:0)
如果您已经为您的Currency
类型实现了有效的模型绑定器,则只需实现一个IModelBinderProvider
即可在MVC需要绑定到Currency
时自动提供模型绑定器类型:
public class CurrencyModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(Currency))
return new BinderTypeModelBinder(typeof(CurrencyModelBinder));
return null;
}
}
然后,您需要在初创公司的ConfigureServices
中注册它:
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new CurrencyModelBinderProvider());
});
然后,所有Currency
元素都将使用您的CurrencyModelBinder
自动绑定,而无需在所有地方都使用[ModelBinder]
属性。
文档的“custom model binder sample” section中也对此进行了描述。
仅出于完整性考虑,可能会实施CurrencyModelBinder
:
public class CurrencyModelBinder : IModelBinder
{
private static readonly Currency[] _currencies = new Currency[]
{
Currency.EUR,
Currency.USD,
};
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelName = bindingContext.ModelName;
var providerResult = bindingContext.ValueProvider.GetValue(modelName);
if (providerResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
var value = providerResult.FirstValue;
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
var currency = _currencies
.FirstOrDefault(c => c.Code.Equals(value, StringComparison.OrdinalIgnoreCase));
if (currency != null)
bindingContext.Result = ModelBindingResult.Success(currency);
else
bindingContext.ModelState.TryAddModelError(modelName, "Unknown currency");
return Task.CompletedTask;
}
}
答案 1 :(得分:0)
我尝试此实现,但遇到相同的错误。
我在CurrencyModelBinder和CurrencyModelBinderProvider中设置了断点
Breakpoint on Model Binder Provider
问题出在比较中: context.Metadata.ModelType =“模板” 并且仅针对Currency调用CurrencyModelBinder。
我用这种方法解决了:
使用JsonConverter反序列化
[HttpPost, ODataRoute("Templates")]
public IActionResult Insert([FromBody] object value)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var template = JsonConvert.DeserializeObject<Template>(value.ToString());
template.Id = Guid.NewGuid();
_context.Templates.Add(template);
_context.SaveChanges();
return Created(value);
}
货币类现在为
[Serializable]
[JsonConverter(typeof(CurrencyJsonConverter))]
public class Currency : StringEnumeration<Currency>
{
public static Currency CHF = new Currency("CHF", "CHF");
public static Currency EUR = new Currency("EUR", "EUR");
public static Currency USD = new Currency("USD", "USD");
Currency() { }
Currency(string code, string description) : base(code, description) { }
}
和JsonConverter
public class CurrencyJsonConverter : JsonConverter
{
public override bool CanWrite => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Currency);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = reader.Value as string;
return Currency.Parse(value);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Currency currency)
serializer.Serialize(writer, currency.Code);
}
}
我不明白为什么Defaul Model Binder不使用Json反序列化。
我一直在等待您的友好答复。