ASP.net MVC返回JSONP


72

我正在寻找跨域返回一些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
    };
}

奇迹般有效。


2
我刚刚写了一篇关于这一确切内容的博客文章,并使用了与您上面概述的基本相同的方法,除了在顶部添加了一个小动作过滤器,以使在现有控制器实现上启用JSONP的痛苦减轻了。>:您可以在这里阅读所有关于它http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx
拉吉

谢谢!刚刚在我们的项目中实现了!:)
亚当·卡赫塔瓦

3
真好!但JSONP应该被当作application / JavaScript的stackoverflow.com/questions/111302/...
毛里西奥·雅伯

1
+1被引博客是史诗般的胜利。
克里斯·马里西奇

1
这显示了仅发布链接作为答案的问题。随着博客的迁移和放弃,在答案的正文中至少张贴最低限度的内容会增加StackOverflow的价值。
罗伯特·考彻

Answers:


17

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

使用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");
 }

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。


3
什么是JsonpResult类?

你能举个例子吗?
乔什·诺

3
对于其他评论者,需要明确的是,求职者正在从OP中构建代码。
鲁芬

10

Ranju的博客文章(又名“我发现的此博客文章”)非常出色,阅读该文章将使您能够进一步研究以下解决方案,以便您的控制器可以在同一控制器操作中优雅地处理同域JSON和跨域JSONP请求,而无需附加代码[操作中]。

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

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

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

这个出色的博客文章中找到了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
        }
    }
}

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


1
谢谢@ruffin!这些天之一就是要这样做。感谢您完成!:)
Raj

1

对于ASP.NET Core,不是ASP.NET MVC
这是为答案中存在的解决方案的ASP.NET CORE量身定制的版本

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


0

stimms和ranju v所引用的文章都非常有用,并且使情况很清楚。

但是,我在使用扩展,在网上找到的MVC代码的上下文中进行子类化方面无所适从。

有两个关键要点吸引了我:

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

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

令我感到困惑的是,我确实在寻找一种无需重新编码ExecuteResult即可派生或扩展JsonResult的方法。由于JSONP实际上是带有前缀和后缀的JSON字符串,因此似乎很浪费。但是,最下面的ExecuteResult使用respone.write-因此,最安全的更改方法是重新编码ExecuteResults,就像各种帖子提供的一样!

如果可以,我可以发布一些代码,但是此线程中已经有很多代码。


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
您能否提供更多详细信息,而不仅是仅提供代码的答案?
托马斯

-2

上面的解决方案是一种很好的工作方式,但是应该使用新的结果类型扩展它,而不要使用返回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>");
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.