如何在asp.net核心中验证json请求体是否为有效的json

问题描述 投票:1回答:1

在asp.net核心2.1中,当控制器动作设置为:

    [HttpPost]
    public JsonResult GetAnswer(SampleModel question)
    {               
        return Json(question.Answer);
    }

其中SampleModel定义为:

public class SampleModel
{
    [Required]
    public string Question { get; set; }

    public string Answer { get; set; }
}

这仍被视为有效请求:

{
  "question": "some question",
  "question": "some question 2",
  "answer": "some answer"
}

在控制器中,我可以看到第二个问题是模型和模型的价值是有效的。

问题是如何在模型绑定之前将请求主体验证为有效的JSON?

c# asp.net-core asp.net-core-2.1 model-validation
1个回答
2
投票

Timothy Shields's answer说,如果我们有重复的属性键,很难说这将是一个无效的json。

当使用ASP.NET Core 2.1时,它根本不会扔掉。

12.0.1开始,Newtonsoft.Json有一个DuplicatePropertyNameHandling settings。如果我们设置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
        }
    }

演示:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.