ASP.net MVC 返回 JSONP

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/758879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 17:13:56  来源:igfitidea点击:

ASP.net MVC returning JSONP

jsonasp.net-mvcjsonp

提问by stimms

I am looking to return some JSON across domains and I understand that the way to do this is through JSONP rather than pure JSON. I am using ASP.net MVC so I was thinking about just extending the JSONResult type and then extendig Controller so that it also implemented a Jsonp method. Is this the best way to go about it or is there a built in ActionResult which might be better?

我希望跨域返回一些 JSON,我知道这样做的方法是通过 JSONP 而不是纯 JSON。我正在使用 ASP.net MVC,所以我正在考虑只扩展 JSONResult 类型,然后扩展控制器,以便它也实现了一个 Jsonp 方法。这是最好的方法还是有一个内置的 ActionResult 可能更好?

Edit: I went ahead and did that. Just for reference sake I added a new result:

编辑:我继续这样做了。仅供参考,我添加了一个新结果:

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

and also a couple of methods to a superclass of all my controllers:

以及我所有控制器的超类的几种方法:

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

Works like a charm.

奇迹般有效。

回答by Maksym Kozlenko

Here is a simple solution, if you don't want to define an action filter

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

Client side code using jQuery:

使用 jQuery 的客户端代码:

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

MVC controller action. Returns content result with JavaScript code executing callback function provided with query string. Also sets JavaScript MIME type for response.

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

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

回答by mendicant

Rather than subclassing my controllers with Jsonp() methods, I went the extension method route as it feels a touch cleaner to me. The nice thing about the JsonpResult is that you can test it exactly the same way you would a JsonResult.

我没有使用 Jsonp() 方法子类化我的控制器,而是使用了扩展方法路由,因为它对我来说感觉更干净。JsonpResult 的好处是您可以像测试 JsonResult 一样测试它。

I did:

我做了:

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

This way you don't have to worry about creating all the different Jsonp() overloads, just convert your JsonResult to a Jsonp one.

这样您就不必担心创建所有不同的 Jsonp() 重载,只需将您的 JsonResult 转换为 Jsonp 一个。

回答by ruffin

Ranju's blog post(aka "This blog post I found") is excellent, and reading it will allow you to further the solution below so that your controller can handle same-domain JSON and cross-domain JSONP requests elegantly in the same controller action without additional code [in the action].

Ranju 的博文(又名“我发现的这篇博文”)非常出色,阅读它可以让您进一步解决下面的问题,以便您的控制器可以在同一个控制器操作中优雅地处理同域 JSON 和跨域 JSONP 请求,而无需附加代码[在行动中]。

Regardless, for the "give me the code" types, here it is, in case the blog disappears again.

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

In your controller (this snippet is new/non-blog code):

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

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

JsonpResult found on this excellent blog post:

JsonpResult 在 这篇优秀的博客文章中找到

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

Note:Following up on the comments to the OP by @Ranju and others, I figured it was worth posting the "bare minimum" functional code from Ranju's blog post as a community wiki. Though it's safe to say that Ranju added the above and other code on his blog to be used freely, I'm not going to copy his words here.

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

回答by K.Hicham

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

回答by From Orbonia

The referenced articles by stimms and ranju v were both very useful and made the situation clear.

stimms 和 Ranju v 引用的文章都非常有用,并且清楚地说明了情况。

However, I was left scratching my head about using extensions, sub-classing in context of the MVC code I had found online.

然而,我在网上找到的 MVC 代码的上下文中使用扩展和子类化让我摸不着头脑。

There was two key points that caught me out:

有两个关键点吸引了我:

  1. The code I had derived from ActionResult, but in ExecuteResult there was some code to return either XML or JSON.
  2. I had then created a Generics based ActionResult, to ensure the same ExecuteResults was used independant of the type of data I returned.
  1. 我从 ActionResult 派生的代码,但在 ExecuteResult 中有一些代码可以返回 XML 或 JSON。
  2. 然后我创建了一个基于泛型的 ActionResult,以确保独立于我返回的数据类型使用相同的 ExecuteResults。

So, combining the two - I did not need further extensions or sub-classing to add the mechanism to return JSONP, simply change my existing ExecuteResults.

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

What had confused me is that really I was looking for a way to derive or extend JsonResult, without re-coding the ExecuteResult. As JSONP is effectively a JSON string with prefix & suffix it seemed a waste. However the underling ExecuteResult uses respone.write - so the safest way of changing is to re-code ExecuteResults as handily provided by various postings!

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

I can post some code if that would be useful, but there is quite a lot of code in this thread already.

如果有用的话,我可以发布一些代码,但是这个线程中已经有很多代码了。

回答by mounir

the solution above is a good way of working but it should be extendend with a new type of result instead of having a method that returns a JsonResult you should write methods that return your own result types

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