ASP.net MVC 返回 JSONP

发布于 2024-07-17 00:38:39 字数 1919 浏览 6 评论 0原文

我希望跨域返回一些 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
    };
}

工作起来就像一个魅力。

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 extending the 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 that might be better?


Solution: 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

淡笑忘祈一世凡恋 2024-07-24 00:38:40
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);
}
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);
}
停顿的约定 2024-07-24 00:38:40

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

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

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>");
    }
}
趴在窗边数星星i 2024-07-24 00:38:39

如果您不想

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

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

Client side code using 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.

 public ContentResult JsonpCall(string callback)
 {
      return Content(String.Format("{0}({1});",
          callback, 
          new JavaScriptSerializer().Serialize(new { a = 1 })),    
          "application/javascript");
 }
安稳善良 2024-07-24 00:38:39

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

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.

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.

时光瘦了 2024-07-24 00:38:39

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 在他的博客上添加了上述和其他代码以供自由使用,但我不会在这里复制他的话。

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].

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:

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

挽袖吟 2024-07-24 00:38:39

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

For ASP.NET Core ,NOT ASP.NET MVC
This is a tailored version for ASP.NET CORE of the solution which exists in the answer

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 = 
quot;{request.Query["callback"]}({serializedJson})";
            await response.WriteAsync(result);
        }
    }
}

温柔少女心 2024-07-24 00:38:39

stimms 和 ranju v 引用的文章都非常有用,并且使情况变得清晰。

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

有两个关键点让我困惑:

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

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

让我困惑的是,我实际上正在寻找一种派生或扩展 JsonResult 的方法,而无需重新编码 ExecuteResult。 由于 JSONP 实际上是一个带有前缀 & 的 JSON 字符串。 后缀看起来很浪费。 然而,底层 ExecuteResult 使用 respone.write - 因此最安全的更改方法是重新编码 ExecuteResults,如各种帖子轻松提供的那样!

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

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

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

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.

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.

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!

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文