使用 JSON.NET 返回 ActionResult
使用 JSON.NET 返回 ActionResult
我在尝试编写一个C#方法,它可以序列化一个模型并返回一个JSON结果。这里是我的代码:
public ActionResult Read([DataSourceRequest] DataSourceRequest request) { var items = db.Words.Take(1).ToList(); JsonSerializerSettings jsSettings = new JsonSerializerSettings(); jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; var converted = JsonConvert.SerializeObject(items, null, jsSettings); return Json(converted, JsonRequestBehavior.AllowGet); }
当我在Chrome中访问Words/Read时,我得到了以下JSON结果:
"[{\"WordId\":1,\"Rank\":1,\"PartOfSpeech\":\"article\",\"Image\":\"Upload/29/1/Capture1.PNG\",\"FrequencyNumber\":\"22038615\",\"Article\":null,\"ClarificationText\":null,\"WordName\":\"the | article\",\"MasterId\":0,\"SoundFileUrl\":\"/UploadSound/7fd752a6-97ef-4a99-b324-a160295b8ac4/1/sixty_vocab_click_button.mp3\",\"LangId\":1,\"CatId\":null,\"IsActive\":false}"
我认为\"转义引号是一个问题,当你对一个对象进行双重序列化时会出现这个问题。根据其他问题的回答:
WCF JSON output is getting unwanted quotes & backslashes added
这确实看起来像是我对对象进行了双重序列化,因为我首先使用JSON.NET进行了序列化,然后将结果传递给Json()函数。为了避免引用循环,我需要手动进行序列化,但我认为我的视图需要一个ActionResult。
在这里如何返回一个ActionResult?我需要这样做吗,还是可以只返回一个字符串?
使用JSON.NET返回ActionResult的问题出现的原因是,想要替换控制器中默认的JSON序列化方式。解决方法是通过重写控制器中的Json()方法,并创建一个JsonNetResult类来自定义JSON序列化过程。
首先,在控制器(或基类控制器)中重写Json()方法,代码如下:
protected override JsonResult Json( object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior) { return new JsonNetResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior }; }
然后,创建JsonNetResult类,代码如下:
public class JsonNetResult : JsonResult { public JsonNetResult() { Settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }; } public JsonSerializerSettings Settings { get; private set; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && "GET".Equals( context.HttpContext.Request.HttpMethod, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("JSON GET is not allowed"); } HttpResponseBase response = context.HttpContext.Response; response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; if (this.ContentEncoding != null) response.ContentEncoding = this.ContentEncoding; if (this.Data == null) return; var scriptSerializer = JsonSerializer.Create(this.Settings); using (var sw = new StringWriter()) { scriptSerializer.Serialize(sw, this.Data); response.Write(sw.ToString()); } } }
通过这样的方式,当在控制器中调用Json()方法时,将自动使用JSON.NET进行序列化。
此外,还有一个问题是如何在调用Json()方法时传递JsonRequestBehavior.AllowGet参数。可以通过在Json()方法中添加重载来实现,该重载只接受data和behavior参数,并将contentType和contentEncoding参数设置为null,然后使用这些参数调用实现方法。代码如下:
protected override JsonResult Json( object data, JsonRequestBehavior behavior) { return Json(data, null, null, behavior); }
另外,某些情况下了在JsonNetResult类中对AllowGet的限制似乎与应用于方法的属性重复,他想知道如何从ControllerContext中读取AcceptVerbsAttribute。这个问题没有得到明确的回答。
还有一个人提到了关于序列化后直接写入输出流是否更高效的问题。他给出了一个使用StreamWriter和response.OutputStream来直接写入输出流的代码示例。但是,并没有给出明确的答案。
使用JSON.NET来返回ActionResult的问题可以通过重写Json()方法和创建JsonNetResult类来解决。这样可以自定义JSON序列化过程,并实现更灵活、高效的JSON序列化。
问题出现的原因是返回的响应头中的content-type为"text/html",而不是"application/json"。解决方法是使用Json.NET库的Content方法来返回ActionResult,并指定content-type为"application/json"。
根据stackoverflow上的答案建议,可以使用以下代码来解决问题:
return Content(converted, "application/json");
这个解决方法非常简单,能够实现需求。
从hubatish的答案中可以得到启发,我们可以创建一个基于Controller的抽象类,其中包含一个JsonNetResult方法来返回ActionResult。具体代码如下:
public abstract class ControllerBase : Controller
{
protected const string JsonEncoding = "application/json; charset=utf-8";
protected ContentResult JsonNetResult(object result)
{
string json = JsonConvert.SerializeObject(result);
return Content(json, JsonEncoding);
}
}
也许在过去的四年间发生了一些变化,但是根据这个答案中的语法,我的响应头中显示的content-type为"application/json"。