根据Timothy Shields的回答,如果我们有重复的属性键,很难说这将是一个无效的JSON。
当使用ASP.NET Core 2.1
时,它根本不会抛出异常。
截至版本12.0.1
,Newtonsoft.Json具有DuplicatePropertyNameHandling设置。如果我们设置DuplicatePropertyNameHandling.Error
并传递了重复的属性,则它将抛出异常。因此,我能想到的最简单的方法是创建自定义模型绑定器。我们可以反序列化JSON并更改ModelState(如果它抛出异常)。
首先,安装最新的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;
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));
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());
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{
}
}
演示:
(注:以上内容涉及IT技术)