有没有办法让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);
}
}