在 MVC 控制器中将 JSON 绑定到 JToken



有没有办法让MVC控制器将传入的动态JSON绑定到JToken对象?

如果我使用 API 控制器,我可以这样做:

public class TestController : ApiController
{
    public void Post(JToken json)
    {
    }
}

并且发布的 JSON 将转换为 JToken 对象。但是,如果我使用 MVC 控制器,则会导致服务器错误。

public class TestController : Controller
{
    [HttpPost]
    public ActionResult TestAction(JToken json)
    {
        return new HttpStatusCodeResult(HttpStatusCode.OK);
    }
}

我意识到还有其他方法可以获取传入的数据,但我更愿意在 MVC 控制器中将其作为 JToken 接收。

我尝试从这里使用自定义ValueProviderFactory,但我仍然收到从我的 AJAX 调用返回的服务器错误:

$.ajax({
    url: '/Test/TestAction',    //or /api/Test
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({foo:"bar",wibble:"wobble"})
}).done(function (res) {
    alert('ok');
}).fail(function (xhr, status, error) {
    alert('error')
});

更新:

注意 - 如上所述,我已将默认JsonValueProviderFactory替换为基于 Json.NET 的默认。

经过进一步调查,问题似乎出现在DefaultModelBinder.CreateModel方法中。当DefaultModelBinder尝试创建JToken实例时,它会失败JToken因为它是一个抽象类。即使我将 TestAction 参数更改为 JObject,它仍然失败,大概是因为对象层次结构下方有JToken属性。

在这种特殊情况下,通过编写自定义ValueProviderFactory来更改传入 JSON 的默认序列化程序不起作用。这似乎是因为JToken是一个抽象类,并且默认的 ModelBinder 无法创建涉及抽象类的模型实例。

解决方案是为操作创建自定义ModelBinder

public class JsonNetModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (!IsJSONRequest(controllerContext))
        {
            return base.BindModel(controllerContext, bindingContext);
        }
        var request = controllerContext.HttpContext.Request;
        request.InputStream.Seek(0, SeekOrigin.Begin);
        var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();
        return JsonConvert.DeserializeObject(jsonStringData, bindingContext.ModelType);
    }
    private static bool IsJSONRequest(ControllerContext controllerContext)
    {
        var contentType = controllerContext.HttpContext.Request.ContentType;
        return contentType.Contains("application/json");
    }
}

并在操作上使用自定义ModelBinder,如下所示:

public class TestController : Controller
{
    [HttpPost]
    public ActionResult TestAction([ModelBinder(typeof(JsonNetModelBinder))] JToken json)
    {
        return new HttpStatusCodeResult(HttpStatusCode.OK);
    }
}

相关内容

  • 没有找到相关文章

最新更新