Согласно ответу Тимоти Шилдса , трудно сказать, что это был бы неверный json, если бы мы дублировали ключи свойств.
А при использовании ASP.NET Core 2.1
он вообще не скинет.
Начиная с 12.0.1
, Newtonsoft.Json имеет настройки DuplicatePropertyNameHandling . Он выдаст, если мы установим DuplicatePropertyNameHandling.Error
и передадим дублированное свойство. Таким образом, самый простой способ, которым я могу придумать, - это создать пользовательское связующее для моделей. Мы могли бы десериализовать JSON и изменить ModelState, если он выбрасывает.
Fristly, установите последнюю версию Newtonsoft.Json
:
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
и затем зарегистрируйте опцию JsonLoadSettings
в качестве одноэлементной службы для последующего повторного использования:
services.AddSingleton<JsonLoadSettings>(sp =>{
return new JsonLoadSettings {
DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Error,
};
});
Теперь мы можем создать привязку пользовательской модели для работы с дублированными свойствами:
public class XJsonModelBinder: IModelBinder
{
private JsonLoadSettings _loadSettings;
public XJsonModelBinder(JsonLoadSettings loadSettings)
{
this._loadSettings = loadSettings;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
var modelName = bindingContext.BinderModelName?? "XJson";
var modelType = bindingContext.ModelType;
// create a JsonTextReader
var req = bindingContext.HttpContext.Request;
var raw= req.Body;
if(raw == null){
bindingContext.ModelState.AddModelError(modelName,"invalid request body stream");
return Task.CompletedTask;
}
JsonTextReader reader = new JsonTextReader(new StreamReader(raw));
// binding
try{
var json= (JObject) JToken.Load(reader,this._loadSettings);
var o = json.ToObject(modelType);
bindingContext.Result = ModelBindingResult.Success(o);
}catch(Exception e){
bindingContext.ModelState.AddModelError(modelName,e.ToString()); // you might want to custom the error info
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
Чтобы разрешить чтение Request.Body
несколько раз, мы также можем создать пустышку Filter
:
public class EnableRewindResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.HttpContext.Request.EnableRewind();
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
Наконец, украсьте метод действия с помощью [ModelBinder(typeof(XJsonModelBinder))]
и EnableRewindResourceFilter
:
[HttpPost]
[EnableRewindResourceFilter]
public JsonResult GetAnswer([ModelBinder(typeof(XJsonModelBinder))]SampleModel question)
{
if(ModelState.IsValid){
return Json(question.Answer);
}
else{
// ... deal with invalid state
}
}
Демо: