2011-08-18 9 views
95

Có thể sử dụng JSON.NET làm trình nối tiếp JSON mặc định trong ASP.NET   MVC   3 không?Sử dụng JSON.NET làm trình nối tiếp JSON mặc định trong ASP.NET MVC 3 - có thể không?

Theo nghiên cứu của tôi, có vẻ như cách duy nhất để thực hiện điều này là để extend ActionResult như JsonResult in MVC3 is not virtual ...

tôi hy vọng rằng với ASP.NET MVC     3 rằng sẽ có một cách để chỉ định một nhà cung cấp có thể cắm được để tuần tự hóa thành JSON.

Suy nghĩ?

+0

liên quan: http://stackoverflow.com/questions/6883204/change-default-json-serializer-used-in-asp- mvc3 –

Trả lời

98

Tôi tin rằng cách tốt nhất để làm điều đó, là - như được mô tả trong các liên kết của bạn - để mở rộng ActionResult hoặc mở rộng JsonResult trực tiếp.

Đối với phương thức JsonResult không ảo trên bộ điều khiển không đúng, chỉ cần chọn đúng tình trạng quá tải. Này hoạt động tốt:

protected override JsonResult Json(object data, string contentType, Encoding contentEncoding) 

EDIT 1: Một phần mở rộng JsonResult ...

public class JsonNetResult : JsonResult 
{ 
    public override void ExecuteResult(ControllerContext context) 
    { 
     if (context == null) 
      throw new ArgumentNullException("context"); 

     var response = context.HttpContext.Response; 

     response.ContentType = !String.IsNullOrEmpty(ContentType) 
      ? ContentType 
      : "application/json"; 

     if (ContentEncoding != null) 
      response.ContentEncoding = ContentEncoding; 

     // If you need special handling, you can call another form of SerializeObject below 
     var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented); 
     response.Write(serializedObject); 
    } 

EDIT 2: Tôi đã gỡ bỏ việc kiểm tra cho Dữ liệu đang được null theo những gợi ý dưới đây. Điều đó sẽ làm cho các phiên bản mới hơn của JQuery hạnh phúc và có vẻ như là điều lành mạnh để làm, vì câu trả lời sau đó có thể được deserialized vô điều kiện. Tuy nhiên, hãy lưu ý rằng đây không phải là hành vi mặc định cho các phản hồi JSON từ ASP.NET MVC, mà thay vì trả lời bằng một chuỗi rỗng, khi không có dữ liệu.

+0

Cảm ơn - Tôi mặc dù tôi đã đi đúng hướng, và bạn xác nhận nó ... Đối với JsonResult không được ảo, tôi thậm chí không bận tâm để kiểm tra các chữ ký phương thức khác :), tôi chỉ lấy nó như được cấp dựa trên nhận xét từ Maxim tại đây - http://stackoverflow.com/questions/6883204/change-default-json-serializer-used-in-asp-mvc3/6884503#6884503 – zam6ak

+1

Mã đề cập đến MySpecialContractResolver, không được xác định. Câu hỏi này giúp với điều đó (và rất liên quan đến vấn đề tôi phải giải quyết): http://stackoverflow.com/questions/6700053/json-net-read-only-properties-support-for-ignoredatamember – Elliveny

+0

Ồ vâng, thiết lập một ContractResolver đặc biệt như tôi làm trong đoạn mã trên, thực sự không có gì để làm với giải pháp cho câu hỏi này - chỉ xảy ra là, những gì tôi cần lúc đó :) Cảm ơn bạn đã chỉ ra điều đó. Bộ nối tiếp có thể được cấu hình theo bất kỳ cách nào phù hợp. – asgerhallas

21

Tôi biết điều này là tốt sau khi câu hỏi đã được trả lời, nhưng tôi đang sử dụng một cách tiếp cận khác như tôi đang sử dụng tiêm phụ thuộc để nhanh chóng điều khiển của tôi.

Tôi đã thay thế IActionInvoker (bằng cách tiêm thuộc tính ControllerActionInvoker của bộ điều khiển) với phiên bản ghi đè phương thức InvokeActionMethod.

Điều này có nghĩa không có sự thay đổi để điều khiển thừa kế và nó có thể dễ dàng loại bỏ khi tôi nâng cấp lên MVC4 bằng cách thay đổi đăng ký container DI cho TẤT CẢ các bộ điều khiển

public class JsonNetActionInvoker : ControllerActionInvoker 
{ 
    protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) 
    { 
     ActionResult invokeActionMethod = base.InvokeActionMethod(controllerContext, actionDescriptor, parameters); 

     if (invokeActionMethod.GetType() == typeof(JsonResult)) 
     { 
      return new JsonNetResult(invokeActionMethod as JsonResult); 
     } 

     return invokeActionMethod; 
    } 

    private class JsonNetResult : JsonResult 
    { 
     public JsonNetResult() 
     { 
      this.ContentType = "application/json"; 
     } 

     public JsonNetResult(JsonResult existing) 
     { 
      this.ContentEncoding = existing.ContentEncoding; 
      this.ContentType = !string.IsNullOrWhiteSpace(existing.ContentType) ? existing.ContentType : "application/json"; 
      this.Data = existing.Data; 
      this.JsonRequestBehavior = existing.JsonRequestBehavior; 
     } 

     public override void ExecuteResult(ControllerContext context) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException("context"); 
      } 
      if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet) && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
      { 
       base.ExecuteResult(context);       // Delegate back to allow the default exception to be thrown 
      } 

      HttpResponseBase response = context.HttpContext.Response; 
      response.ContentType = this.ContentType; 

      if (this.ContentEncoding != null) 
      { 
       response.ContentEncoding = this.ContentEncoding; 
      } 

      if (this.Data != null) 
      { 
       // Replace with your favourite serializer. 
       new Newtonsoft.Json.JsonSerializer().Serialize(response.Output, this.Data); 
      } 
     } 
    } 
} 

--- EDIT - Cập nhật để hiển thị đăng ký bình chứa để điều khiển . Tôi đang sử dụng Unity ở đây.

private void RegisterAllControllers(List<Type> exportedTypes) 
{ 
    this.rootContainer.RegisterType<IActionInvoker, JsonNetActionInvoker>(); 
    Func<Type, bool> isIController = typeof(IController).IsAssignableFrom; 
    Func<Type, bool> isIHttpController = typeof(IHttpController).IsAssignableFrom; 

    foreach (Type controllerType in exportedTypes.Where(isIController)) 
    { 
     this.rootContainer.RegisterType(
      typeof(IController), 
      controllerType, 
      controllerType.Name.Replace("Controller", string.Empty), 
      new InjectionProperty("ActionInvoker") 
     ); 
    } 

    foreach (Type controllerType in exportedTypes.Where(isIHttpController)) 
    { 
     this.rootContainer.RegisterType(typeof(IHttpController), controllerType, controllerType.Name); 
    } 
} 

public class UnityControllerFactory : System.Web.Mvc.IControllerFactory, System.Web.Http.Dispatcher.IHttpControllerActivator 
{ 
    readonly IUnityContainer container; 

    public UnityControllerFactory(IUnityContainer container) 
    { 
     this.container = container; 
    } 

    IController System.Web.Mvc.IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) 
    { 
     return this.container.Resolve<IController>(controllerName); 
    } 

    SessionStateBehavior System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName) 
    { 
     return SessionStateBehavior.Required; 
    } 

    void System.Web.Mvc.IControllerFactory.ReleaseController(IController controller) 
    { 
    } 

    IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
    { 
     return this.container.Resolve<IHttpController>(controllerType.Name); 
    } 
} 
+2

+1 cho giải pháp "không phô trương". – georgiosd

+0

Rất đẹp, nhưng bạn sử dụng nó như thế nào? Hay tốt hơn làm thế nào bạn tiêm nó? – Adaptabi

+1

đã thêm triển khai thêm –

22

JSON chuyển đổi sử dụng Newtonsoft của:

public ActionResult DoSomething() 
{ 
    dynamic cResponse = new ExpandoObject(); 
    cResponse.Property1 = "value1"; 
    cResponse.Property2 = "value2"; 
    return Content(JsonConvert.SerializeObject(cResponse)); 
} 
+4

Không chắc chắn nếu điều này là hacky hay không, nhưng crap thánh là nó dễ dàng hơn so với việc tạo các lớp mở rộng, chỉ để trả lại một chuỗi json ngu ngốc. –

51

tôi thực hiện điều này mà không cần một bộ điều khiển cơ sở hoặc tiêm.

Tôi đã sử dụng bộ lọc hành động để thay thế JsonResult bằng JsonNetResult.

public class JsonHandlerAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     var jsonResult = filterContext.Result as JsonResult; 

     if (jsonResult != null) 
     { 
      filterContext.Result = new JsonNetResult 
      { 
       ContentEncoding = jsonResult.ContentEncoding, 
       ContentType = jsonResult.ContentType, 
       Data = jsonResult.Data, 
       JsonRequestBehavior = jsonResult.JsonRequestBehavior 
      }; 
     } 

     base.OnActionExecuted(filterContext); 
    } 
} 

Trong Global.asax.cs Application_Start(), bạn sẽ cần phải thêm:

GlobalFilters.Filters.Add(new JsonHandlerAttribute()); 

Vì hoàn của, đây là lớp gia hạn JsonNetResult của tôi mà tôi nhặt từ một nơi khác và rằng tôi chút thay đổi để có được hỗ trợ hấp đúng:

public class JsonNetResult : JsonResult 
{ 
    public JsonNetResult() 
    { 
     Settings = new JsonSerializerSettings 
     { 
      ReferenceLoopHandling = ReferenceLoopHandling.Error 
     }; 
    } 

    public JsonSerializerSettings Settings { get; private set; } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     if (context == null) 
      throw new ArgumentNullException("context"); 
     if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", 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); 
     scriptSerializer.Serialize(response.Output, this.Data); 
    } 
} 
+0

Đây là một giải pháp tốt đẹp. Làm cho nó trở nên bản địa 'return Json()' có hiệu lực sử dụng Json.Net. – OneHoopyFrood

+0

Đối với bất cứ ai tự hỏi nó hoạt động như thế nào, nó chặn 'JsonResult' từ' Json() 'và chuyển nó thành' JsonNetResult'. Nó làm như vậy bằng cách sử dụng từ khóa 'as' trả về null nếu chuyển đổi là không thể. Rất tiện lợi. 10 điểm cho Gryffindor! – OneHoopyFrood

+3

Câu hỏi mặc dù, không serializer mặc định chạy trên đối tượng trước khi nó bị chặn? – OneHoopyFrood

12

Mở rộng câu trả lời từ https://stackoverflow.com/users/183056/sami-beyoglu, nếu bạn đặt loại Nội dung, thì jQuery sẽ có thể chuyển đổi dữ liệu trả về thành đối tượng cho bạn.

public ActionResult DoSomething() 
{ 
    dynamic cResponse = new ExpandoObject(); 
    cResponse.Property1 = "value1"; 
    cResponse.Property2 = "value2"; 
    return Content(JsonConvert.SerializeObject(cResponse), "application/json"); 
} 
+0

Cảm ơn bạn, tôi có một kết hợp lai và đây là điều duy nhất có thể làm việc cho tôi. –

+0

Tôi đã sử dụng điều này với JSON.NET như sau: 'JObject jo = GetJSON(); trả lại Nội dung (jo.ToString(), "application/json"); ' –

+0

Đơn giản và Tuyệt vời. – iMatoria

3

Tôi đã tạo phiên bản giúp hành động dịch vụ web an toàn và đơn giản. Bạn sử dụng nó như thế này:

public JsonResult<MyDataContract> MyAction() 
{ 
    return new MyDataContract(); 
} 

Lớp:

public class JsonResult<T> : JsonResult 
{ 
    public JsonResult(T data) 
    { 
     Data = data; 
     JsonRequestBehavior = JsonRequestBehavior.AllowGet; 
    } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     // Use Json.Net rather than the default JavaScriptSerializer because it's faster and better 

     if (context == null) 
      throw new ArgumentNullException("context"); 

     var response = context.HttpContext.Response; 

     response.ContentType = !String.IsNullOrEmpty(ContentType) 
      ? ContentType 
      : "application/json"; 

     if (ContentEncoding != null) 
      response.ContentEncoding = ContentEncoding; 

     var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented); 
     response.Write(serializedObject); 
    } 

    public static implicit operator JsonResult<T>(T d) 
    { 
     return new JsonResult<T>(d); 
    } 
} 
+0

nhưng tại sao bạn muốn có một loại JsonResult mạnh mẽ? : D bạn mất kết quả loại vô danh và kiếm được không có gì ở phía khách hàng, vì nó không sử dụng C# classses anyway? – mikus

+1

@mikus Nó là typesafe ở phía máy chủ: phương thức phải trả về kiểu MyDataContract. Nó làm cho nó rõ ràng cho phía khách hàng chính xác những gì cấu trúc dữ liệu đang được trả lại. Nó cũng ngắn gọn và dễ đọc - JsonResult tự động chuyển đổi bất kỳ loại nào được trả lại cho Json và bạn không phải làm bất cứ điều gì. –