2009-04-17 4 views
69

Я ищу, чтобы вернуть некоторые JSON по доменам, и я понимаю, что способ сделать это через JSONP, а не чистый JSON. Я использую ASP.net MVC, поэтому я думал о просто расширении типа JSONResult, а затем расширителя, чтобы он также реализовал метод Jsonp. Это лучший способ сделать это или есть встроенный ActionResult, который может быть лучше?ASP.net MVC возвращает JSONP

Редактировать: Я пошел вперед и сделал это. Просто ради эталонного я добавил новый результат:

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 
      } 
     } 
    } 

, а также несколько методов для суперкласса всех моих контроллеров:

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 
      }; 
     } 

работает как шарм.

+0

Спасибо! Просто реализовано это в нашем проекте! :) –

+3

Ницца! Но JSONP следует обслуживать как приложение/javascript http://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp –

+0

см. Также 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 –

ответ

13

Вместо того, чтобы подклассифицировать мои контроллеры с помощью методов Jsonp(), я отправил маршрут метода расширения, поскольку он чувствует себя прикосновением для меня. Самое приятное в JsonpResult заключается в том, что вы можете протестировать его точно так же, как и JsonResult.

я сделал:

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}; 
    } 
} 

Таким образом, вам не придется беспокоиться о создании всех различных JSONP() перегрузкам, просто конвертировать JsonResult в JSONP один.

+2

Что такое класс JsonpResult? – Chookoos

+0

Можете ли вы привести пример вызова? –

+2

Другим комментаторам, чтобы быть ясными, нищенка строит код из OP. – ruffin

-2

выше решение является хорошим способом работы, но она должна быть extendend с новым типом результата, вместо того, чтобы иметь метод, который возвращает JsonResult вы должны написать методы, которые возвращают свои собственные типы результатов

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

Связанные статьи с помощью стимулов и рануй были очень полезны и сделали ситуацию ясной.

Тем не менее, я остался почесывать голову об использовании расширений, подклассификация в контексте кода MVC, который я нашел в Интернете.

Там было два ключевых момента, которые попались мне:

  1. код я получен из ActionResult, но в ExecuteResult был некоторый код, который возвращает либо XML или JSON.
  2. Тогда я создал основанный на Generics ActionResult, чтобы гарантировать, что те же ExecuteResults использовались независимо от типа возвращаемых данных.

Итак, объединив два - мне не нужны дальнейшие расширения или подклассы, чтобы добавить механизм для возврата JSONP, просто измените мои существующие ExecuteResults.

Что меня смутило в том, что я действительно искал способ получить или расширить JsonResult, не перекодируя ExecuteResult. Поскольку JSONP - это фактически строка JSON с префиксом &, это казалось пустой тратой. Однако подкласс ExecuteResult использует respone.write - таким образом, самый безопасный способ изменения состоит в том, чтобы перекодировать ExecuteResults так же просто, как и различные публикации!

Я могу опубликовать некоторый код, если бы это было полезно, но в этом потоке уже много кода.

15

Вот простое решение, если вы не хотите, чтобы определить действие фильтра

стороны клиента коды с помощью JQuery:

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

MVC контроллер действий. Возвращает результат содержимого с кодом JavaScript, выполняющим функцию обратного вызова, содержащую строку запроса. Также задает тип JavaScript MIME для ответа.

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 (ака «Этот блог я нашел») отлично, и чтение это позволит вам в дальнейшем решение ниже, чтобы ваш контроллер может работать с тем же доменом JSON и междоменное JSONP запросы элегантно то же действие контроллера без дополнительного кода [в действии].

Независимо от того, для типов «дайте мне код», вот оно, на случай, если блог снова исчезнет.

В контроллере (этот фрагмент нового кода/не блог):

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

JsonpResult найдено на 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 
     } 
    } 
} 

Примечание: Следуя на comments to the OP by @Ranju and others, я полагал, что стоит опубликовать «минимальный» функциональный код из блога Ranju в качестве вики сообщества. Хотя можно с уверенностью сказать, что Ранджу добавил этот код и другой код в своем блоге, который будет использоваться свободно, я не буду копировать его слова здесь.

+1

Спасибо @ruffin! Был смысл сделать это на днях. Спасибо, что сделали! :) – 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

Можете ли вы предоставить более подробную информацию, а не только ответ только на код? – Thomas

Смежные вопросы