【问题标题】:Bind JSON to JToken in MVC Controller在 MVC 控制器中将 JSON 绑定到 JToken
【发布时间】:2016-08-21 19:16:48
【问题描述】:

有没有办法让 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 from here,但我的 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 属性。

【问题讨论】:

标签: asp.net asp.net-mvc asp.net-web-api asp.net-mvc-5 json.net


【解决方案1】:

在这种特殊情况下,将传入 JSON 的默认序列化程序更改为 writing a custom ValueProviderFactory 不起作用。这似乎是因为JToken 是一个抽象类,默认的 ModelBinder 无法创建涉及抽象类的模型实例。

解决方案是create a custom 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");
    }
}

并在 Action 上使用自定义的ModelBinder,如下所示:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-15
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-22
    • 2012-10-03
    相关资源
    最近更新 更多