web-dev-qa-db-ja.com

JSONPを返すASP.net MVC

私はドメイン間でいくつかのJSONを返したいと思っていますが、これを行う方法は純粋なJSONではなくJSONPを使用することであることを理解しています。 ASP.net MVCを使用しているため、JSONResult型を拡張してから、コントローラーを拡張して、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
            };
        }

魅力のように機能します。

71
stimms

アクションフィルターを定義したくない場合の簡単なソリューションを次に示します。

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");
 }
16
Maksym Kozlenko

コントローラを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オーバーロードに変換するだけです。

13
mendicant

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が彼のブログに上記のコードやその他のコードを自由に使用できるように追加したと言っても安全ですが、ここで彼の言葉をコピーするつもりはありません。

10
ruffin

Stimmsとranju vで参照された記事は非常に有用であり、状況を明確にしました。

ただし、拡張機能の使用、オンラインで見つけたMVCコードのコンテキストでのサブクラス化について頭を悩ませていました。

私を惹きつけた2つの重要なポイントがありました。

  1. ActionResultから派生したコードですが、ExecuteResultにはXMLまたはJSONを返すコードがいくつかありました。
  2. その後、ジェネリックベースのActionResultを作成し、返されたデータのタイプに関係なく同じExecuteResultsが使用されるようにしました。

したがって、2つを組み合わせて、JSONPを返すメカニズムを追加するために、拡張機能やサブクラスを追加する必要はなく、単に既存のExecuteResultsを変更します。

私を混乱させたのは、ExecuteResultを再コーディングせずに、JsonResultを派生または拡張する方法を本当に探していたということです。 JSONPは事実上、接頭辞と接尾辞を持つJSON文字列なので、無駄に思えました。しかし、下層のExecuteResultはrespone.writeを使用します-そのため、変更の最も安全な方法は、さまざまな投稿によって便利に提供されるようにExecuteResultsを再コーディングすることです!

役に立つコードがあれば投稿できますが、このスレッドには既に多くのコードがあります。

0
From Orbonia
        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);
            }
0
K.Hicham