2009-04-17 103 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

谢谢!在我们的项目中实现了这个! :) – 2010-02-01 23:47:23

+3

不错!但JSONP应作为应用程序/ JavaScript http://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp – 2011-06-07 15:17:17

+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 – 2011-06-07 15:29:22

回答

13

我没有用Jsonp()方法继承我的控制器,而是使用了扩展方法route,因为它感觉触摸屏更清洁。关于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

上面的解决方案是工作的一个很好的方式,但它应该有一个新的类型的结果,而不是被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

参考文献的刺激和ranju五都是非常有用的,并使情况清楚。

但是,我在使用扩展,在我已经在网上找到的MVC代码的上下文中进行子分类时,感到头疼。

有捉住我出两个关键点:

  1. 我已经从衍生的ActionResult的代码,但在的ExecuteReuslt有一些代码返回XML或JSON。
  2. 然后我创建了一个基于泛型的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的博客文章中的“最低限度”功能代码发布为社区wiki。尽管可以肯定地说Ranju在他的博客上添加了上述代码和其他代码以便自由使用,但我不打算在这里复制他的话。

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