返回 JSONP

我希望能够跨域返回一些 JSON,并且我理解这样做的方法是通过 JSONP 而不是纯 JSON。
我使用的是 ASP.net MVC,所以我只考虑扩展 JsonResult类型,然后扩展 Controller,这样它也实现了一个 Jsonp 方法。
这是最好的方式去做它还是有一个内置的 ActionResult可能更好?


解决方案 : 我继续做了,只是为了参考,我添加了一个新的结果:

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


非常有效。

38944 次浏览

我没有使用 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。

上面的解决方案是一种很好的工作方式,但它应该用一种新类型的 result 进行扩展,而不是使用返回 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>");
}
}

通过 stimms 和 ranju v 的参考文章都是非常有用的,并使情况明确。

然而,对于使用扩展,在我在网上找到的 MVC 代码的上下文中进行子类化,我仍然感到头疼。

有两个关键点引起了我的注意:

  1. 我从 ActionResult 派生的代码,但在 ExecuteResult 中有一些返回 XML 或 JSON 的代码。
  2. 然后,我创建了一个基于 Generics 的 ActionResult,以确保使用的 ExecuteResults 与所返回的数据类型无关。

因此,将两者结合起来——我不需要进一步的扩展或子类化来添加返回 JSONP 的机制,只需更改现有的 ExecuteResults。

让我感到困惑的是,我实际上是在寻找一种方法来派生或扩展 JsonResult,而不需要重新编码 ExecuteResult。由于 JSONP 实际上是一个带前缀和后缀的 JSON 字符串,因此它似乎是一种浪费。然而,底层 ExecuteResult 使用 respone.write-因此,最安全的更改方法是重新编码 ExecuteResults,就像各种发帖所提供的那样!

我可以发布一些代码,如果这将是有用的,但有相当多的代码在这个线程已经。

如果您不想定义动作过滤器,这里有一个简单的解决方案

使用 jQuery 的客户端代码:

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

MVC 控制器动作。返回内容结果,JavaScript 代码执行查询字符串提供的回调函数。还设置响应的 JavaScriptMIME 类型。

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

Ranju 的 blog post (又名“ This blog post I found”)非常出色,阅读它将允许您进一步深入了解下面的解决方案,以便您的控制器能够在同一控制器操作中优雅地处理同一域 JSON 和跨域 JSONP 请求,而不需要额外的代码[在操作中]。

无论如何,对于“给我代码”类型,这里是,以防博客再次消失。

在您的控制器中(此代码片段是新的/非 blog 代码) :

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

结果发现 这篇精彩的博文 :

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

注意: @ Ranju 和其他人对 OP 的评论之后,我认为将 Ranju 博客中的“最低限度”函数代码作为一个社区 wiki 发布是值得的。虽然可以肯定地说,Ranju 在他的博客上添加了上述代码和其他代码,以供自由使用,但我不打算在这里复制他的话。

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

对于 ASP.NET 核心,不是 ASP.NET MVC
这是一个为 ASP.NET 核心的解决方案量身定制的版本,它存在于答案中

public class JsonpResult : JsonResult
{
public JsonpResult(object value) : base(value)
{
}


public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));


HttpResponse response = context.HttpContext.Response;


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


if (Value != null)
{
HttpRequest request = context.HttpContext.Request;
string serializedJson = JsonConvert.SerializeObject(Value);
string result = $"{request.Query["callback"]}({serializedJson})";
await response.WriteAsync(result);
}
}
}