2009-04-17 33 views
69

Tôi đang tìm cách trả về một số JSON trên các tên miền và tôi hiểu rằng cách để làm điều này là thông qua JSONP chứ không phải là JSON thuần túy. Tôi đang sử dụng ASP.net MVC vì vậy tôi đã suy nghĩ về chỉ cần mở rộng các loại JSONResult và sau đó extendig Controller để nó cũng thực hiện một phương pháp Jsonp. Đây có phải là cách tốt nhất để đi về nó hoặc có tích hợp trong ActionResult mà có thể là tốt hơn?ASP.net MVC trả lại JSONP

Chỉnh sửa: Tôi đã tiếp tục và thực hiện điều đó. Chỉ vì tài liệu tham khảo tôi đã thêm một kết quả mới:

public class JsonpResult : System.Web.Mvc.JsonResult 
    { 
     public override void ExecuteResult(ControllerContext context) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException("context"); 
      } 

      HttpResponseBase response = context.HttpContext.Response; 

      if (!String.IsNullOrEmpty(ContentType)) 
      { 
       response.ContentType = ContentType; 
      } 
      else 
      { 
       response.ContentType = "application/javascript"; 
      } 
      if (ContentEncoding != null) 
      { 
       response.ContentEncoding = ContentEncoding; 
      } 
      if (Data != null) 
      { 
       // The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1 
#pragma warning disable 0618 
       HttpRequestBase request = context.HttpContext.Request; 

       JavaScriptSerializer serializer = new JavaScriptSerializer(); 
       response.Write(request.Params["jsoncallback"] + "(" + serializer.Serialize(Data) + ")"); 
#pragma warning restore 0618 
      } 
     } 
    } 

và cũng là một vài phương pháp để một lớp cha của tất cả các bộ điều khiển của tôi:

protected internal JsonpResult Jsonp(object data) 
     { 
      return Jsonp(data, null /* contentType */); 
     } 

     protected internal JsonpResult Jsonp(object data, string contentType) 
     { 
      return Jsonp(data, contentType, null); 
     } 

     protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding) 
     { 
      return new JsonpResult 
      { 
       Data = data, 
       ContentType = contentType, 
       ContentEncoding = contentEncoding 
      }; 
     } 

trình như một say mê.

+0

Cảm ơn! Chỉ cần thực hiện điều này trong dự án của chúng tôi! :) –

+3

Rất tuyệt! Nhưng JSONP phải được phân phối dưới dạng http://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp –

+0

xem thêm http://support.github.com/discussions/api/18 -content-type-should-applicationjavascript-for-jsonp-request http://stackapps.com/questions/1668/wrong-content-type-in-jsonp-calls –

Trả lời

13

Thay vì phân lớp các bộ điều khiển của tôi bằng các phương thức Jsonp(), tôi đã đi tuyến đường phương pháp mở rộng vì nó cảm thấy một trình dọn dẹp cảm ứng với tôi. Điều tốt đẹp về JsonpResult là bạn có thể kiểm tra nó chính xác giống như cách bạn sẽ là một JsonResult.

tôi đã làm:

public static class JsonResultExtensions 
{ 
    public static JsonpResult ToJsonp(this JsonResult json) 
    { 
     return new JsonpResult { ContentEncoding = json.ContentEncoding, ContentType = json.ContentType, Data = json.Data, JsonRequestBehavior = json.JsonRequestBehavior}; 
    } 
} 

Bằng cách này bạn không cần phải lo lắng về việc tạo ra tất cả các quá tải khác nhau JSONP(), chỉ cần chuyển đổi JsonResult của bạn đến một JSONP một.

+2

Lớp học JsonpResult là gì? – Chookoos

+0

Bạn có thể đưa ra ví dụ về một cuộc gọi đến điều này không? –

+2

Đối với những người nhận xét khác, rõ ràng, mendicant đang xây dựng mã nguồn từ OP. – ruffin

-2

giải pháp trên là một cách tốt để làm việc nhưng nó phải được extendend với một loại mới của kết quả thay vì có một phương pháp mà trả về một JsonResult bạn nên viết phương pháp mà trở lại các loại kết quả của riêng bạn

public JsonPResult testMethod() { 
    // use the other guys code to write a method that returns something 
} 

public class JsonPResult : JsonResult 
{ 
    public FileUploadJsonResult(JsonResult data) { 
     this.Data = data; 
    }  

    public override void ExecuteResult(ControllerContext context) 
    { 
     this.ContentType = "text/html"; 
     context.HttpContext.Response.Write("<textarea>"); 
     base.ExecuteResult(context); 
     context.HttpContext.Response.Write("</textarea>"); 
    } 
} 
0

Các bài báo được tham chiếu bởi các kích thích và ranju v đều rất hữu ích và làm cho tình hình trở nên rõ ràng.

Tuy nhiên, tôi đã gãi đầu về việc sử dụng tiện ích mở rộng, phân loại phụ trong bối cảnh mã MVC mà tôi đã tìm thấy trực tuyến.

Có hai điểm quan trọng mà bắt tôi ra:

  1. Code tôi đã có nguồn gốc từ ActionResult, nhưng trong ExecuteResult đã có một số mã để trở về hoặc là XML hoặc JSON.
  2. Sau đó tôi đã tạo một ActionResult dựa trên Generics, để đảm bảo cùng một ExecuteResults được sử dụng độc lập với loại dữ liệu tôi đã trả về.

Vì vậy, kết hợp cả hai - tôi không cần thêm tiện ích mở rộng hoặc phân lớp phụ để thêm cơ chế trả về JSONP, chỉ cần thay đổi ExecuteResults hiện tại của tôi.

Điều khiến tôi nhầm lẫn là thực sự tôi đang tìm cách để lấy được hoặc mở rộng JsonResult, mà không cần phải mã hóa lại ExecuteResult. Vì JSONP là một chuỗi JSON có hiệu quả với tiền tố & hậu tố, nó dường như là một sự lãng phí. Tuy nhiên ExecuteResult underling sử dụng respone.write - vì vậy cách an toàn nhất để thay đổi là viết lại ExecuteResults như được cung cấp bởi các bài đăng khác nhau!

Tôi có thể đăng một số mã nếu điều đó hữu ích nhưng có khá nhiều mã trong chuỗi này.

15

Đây là một giải pháp đơn giản, nếu bạn không muốn xác định một bộ lọc hành động

mã phía khách hàng sử dụng jQuery:

$.ajax("http://www.myserver.com/Home/JsonpCall", { dataType: "jsonp" }).done(function (result) {}); 

MVC controller action. Trả về kết quả nội dung bằng chức năng gọi lại mã JavaScript được cung cấp cùng với chuỗi truy vấn. Cũng đặt loại MIME JavaScript để trả lời.

public ContentResult JsonpCall(string callback) 
{ 
     return Content(String.Format("{0}({1});", 
      callback, 
      new JavaScriptSerializer().Serialize(new { a = 1 })),  
      "application/javascript"); 
} 
10

Ranju's blog post (aka "Bài đăng này trên blog tôi thấy") là tuyệt vời, và đọc nó sẽ cho phép bạn để tiếp tục các giải pháp dưới đây để điều khiển của bạn có thể xử lý cùng một tên miền JSON và JSONP cross-domain yêu cầu thanh lịch trong cùng một hành động điều khiển mà không có mã bổ sung [trong hành động].

Bất kể, đối với loại "cung cấp cho tôi mã", tại đây, trong trường hợp blog biến mất một lần nữa.

Trong điều khiển của bạn (đoạn này là mới mã/phi blog):

[AllowCrossSiteJson] 
public ActionResult JsonpTime(string callback) 
{ 
    string msg = DateTime.UtcNow.ToString("o"); 
    return new JsonpResult 
    { 
     Data = (new 
     { 
      time = msg 
     }) 
    }; 
} 

JsonpResult tìm thấy trên this excellent blog post:

/// <summary> 
/// Renders result as JSON and also wraps the JSON in a call 
/// to the callback function specified in "JsonpResult.Callback". 
/// http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx 
/// </summary> 
public class JsonpResult : JsonResult 
{ 
    /// <summary> 
    /// Gets or sets the javascript callback function that is 
    /// to be invoked in the resulting script output. 
    /// </summary> 
    /// <value>The callback function name.</value> 
    public string Callback { get; set; } 

    /// <summary> 
    /// Enables processing of the result of an action method by a 
    /// custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>. 
    /// </summary> 
    /// <param name="context">The context within which the 
    /// result is executed.</param> 
    public override void ExecuteResult(ControllerContext context) 
    { 
     if (context == null) 
      throw new ArgumentNullException("context"); 

     HttpResponseBase response = context.HttpContext.Response; 
     if (!String.IsNullOrEmpty(ContentType)) 
      response.ContentType = ContentType; 
     else 
      response.ContentType = "application/javascript"; 

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

     if (Callback == null || Callback.Length == 0) 
      Callback = context.HttpContext.Request.QueryString["callback"]; 

     if (Data != null) 
     { 
      // The JavaScriptSerializer type was marked as obsolete 
      // prior to .NET Framework 3.5 SP1 
#pragma warning disable 0618 
      JavaScriptSerializer serializer = new JavaScriptSerializer(); 
      string ser = serializer.Serialize(Data); 
      response.Write(Callback + "(" + ser + ");"); 
#pragma warning restore 0618 
     } 
    } 
} 

Lưu ý: Theo dõi trên comments to the OP by @Ranju and others, tôi figured it was worth đăng bài "tối thiểu" chức năng mã từ bài viết blog của Ranju như là một wiki cộng đồng. Mặc dù thật an toàn khi nói rằng Ranju đã thêm vào đoạn mã trên và mã khác trên blog của anh ấy để được sử dụng một cách tự do, tôi sẽ không sao chép những lời của anh ấy ở đây.

+1

Cảm ơn @ruffin! Có ý nghĩa để làm điều này một trong những ngày này. Cảm ơn bạn đã hoàn thành công việc! :) – Raj

0
 using System; 
     using System.Collections.Generic; 
     using System.Linq; 
     using System.Web; 
     using System.Web.Mvc; 
     using System.Web.Script.Serialization; 

     namespace Template.Web.Helpers 
     { 
      public class JsonpResult : JsonResult 
      { 
       public JsonpResult(string callbackName) 
       { 
        CallbackName = callbackName; 
       } 

       public JsonpResult() 
        : this("jsoncallback") 
       { 
       } 

       public string CallbackName { get; set; } 

       public override void ExecuteResult(ControllerContext context) 
       { 
        if (context == null) 
        { 
         throw new ArgumentNullException("context"); 
        } 

        var request = context.HttpContext.Request; 
        var response = context.HttpContext.Response; 

        string jsoncallback = ((context.RouteData.Values[CallbackName] as string) ?? request[CallbackName]) ?? CallbackName; 

        if (!string.IsNullOrEmpty(jsoncallback)) 
        { 
         if (string.IsNullOrEmpty(base.ContentType)) 
         { 
          base.ContentType = "application/x-javascript"; 
         } 
         response.Write(string.Format("{0}(", jsoncallback)); 
        } 

        base.ExecuteResult(context); 

        if (!string.IsNullOrEmpty(jsoncallback)) 
        { 
         response.Write(")"); 
        } 
       } 
      } 

      public static class ControllerExtensions 
      { 
       public static JsonpResult Jsonp(this Controller controller, object data, string callbackName = "callback") 
       { 
        return new JsonpResult(callbackName) 
        { 
         Data = data, 
         JsonRequestBehavior = JsonRequestBehavior.AllowGet 
        }; 
       } 

       public static T DeserializeObject<T>(this Controller controller, string key) where T : class 
       { 
        var value = controller.HttpContext.Request.QueryString.Get(key); 
        if (string.IsNullOrEmpty(value)) 
        { 
         return null; 
        } 
        JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); 
        return javaScriptSerializer.Deserialize<T>(value); 
       } 
      } 
     } 

//Example of using the Jsonp function:: 
    // 1- 
    public JsonResult Read() 
      { 
       IEnumerable<User> result = context.All();   

       return this.Jsonp(result); 
      } 
    //2- 
    public JsonResult Update() 
      { 
       var models = this.DeserializeObject<IEnumerable<User>>("models"); 
       if (models != null) 
       { 
        Update(models); //Update properties & save change in database 
       } 
       return this.Jsonp(models); 
      } 
+2

bạn có thể vui lòng cung cấp thêm chi tiết, không chỉ là câu trả lời chỉ mã? – Thomas

Các vấn đề liên quan