web-dev-qa-db-ja.com

クロスドメインコール用にASP.NET WebサービスからJSONPを生成するにはどうすればよいですか?

JSONを返すWebサービスを作成し、次のようにjQueryを使用してそれを呼び出そうとしました:

$.ajax({
    contentType: "application/json; charset=utf-8",
    url: "http://examplewebsite.com/service.asmx/GetData",
    data: { projectID: 1 },
    dataType: "jsonp",
    success: function () {alert("success");}
});

ただし、Fiddlerを使用してHTTPトラフィックを確認するときにwebservice呼び出しが成功したにもかかわらず、コードが成功関数を呼び出すことはありません。これは、私のWebサービスがJSONPではなくraw JSONを返しているためだと思います。

次のような標準の.NET Webサービスメソッドからの応答としてJSONPを生成するにはどうすればよいですか。

[WebMethod(), ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public Project GetData(int projectID)
{
    Project p = new Project();
    p.Name = "foobar";
    return p;
}

ありがとう。

20
NickG

OK、私は最終的に自分でそれを理解しました。 Web上で完全な実用的なソリューションを見つけるのは非常に難しいと感じたので、私の実用的なソリューションをここに文書化することにしました。

JSONP応答は、関数呼び出しでラップされた単なる標準のJSON文字列です。 ASP.NETは、この形式で応答を直接返す方法を提供していないようですが、これを自分で行うのは非常に簡単です。ただし、デフォルトのJSONエンコード方式をオーバーライドする必要があります。

以下はJSONPの例です。

functionName({ name: 'value';});

..今このビット:{ name: 'value';}は、JSONシリアライザが提供する標準のJSONなので、関数呼び出しラッパーを追加するだけで済みます。残念ながら、これを行うと、Webサービス関数からオブジェクトを返すときにフレームワークによって透過的に処理される既存のJSONエンコーディングを「アンワイヤ」(またはバイパス)する必要があります。

これは、独自のコードを使用してJSONPを出力ストリーム(Response)に書き込むことにより、Webサービス関数からの応答を完全にオーバーライドすることによって行われます。これは実際には非常に簡単です。以下に例を示します。

組み込みの DataContractJsonSerializer (ASP.NET 3.5+のSystem.Runtime.Serialization.Json名前空間から)または NewtonSoft JSON シリアライザーを使用できます。両方の例は次のとおりです。下に示された。私は組み込みのJSONシリアライザーよりも NewtonSoft JSON (nugetからインストール)を使用することを好みます。これにより、より制御しやすく、デバッグ用に適切にフォーマットされた人間が読めるJSONを出力できることがわかります。紙の上でもはるかに高速です!

[WebMethod()]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public void GetData(int projectID, string callback)
{
    List<Video> videos = null;
    // <code here to populate list on line above>

    // Method 1: use built-in serializer:
    StringBuilder sb = new StringBuilder();
    JavaScriptSerializer js = new JavaScriptSerializer();
    sb.Append(callback + "(");
    sb.Append(js.Serialize(videos));
    sb.Append(");");    

    // Method 2: NewtonSoft JSON serializer (delete as applicable)
    // StringBuilder sb = new StringBuilder();
    // sb.Append(callback + "(");
    // sb.Append(JsonConvert.SerializeObject(videos, Formatting.Indented)); // indentation is just for ease of reading while testing
    // sb.Append(");");     

    Context.Response.Clear();
    Context.Response.ContentType = "application/json";
    Context.Response.Write(sb.ToString());
    Context.Response.End();
}

このメソッドは、次のJQueryコードを使用して呼び出すことができます。

$.ajax({
    crossDomain: true,
    contentType: "application/json; charset=utf-8",
    url: "http://examplewebsite.com/service.asmx/GetData",
    data: { projectID: 1 }, // example of parameter being passed
    dataType: "jsonp",
    success: onDataReceived
});

function onDataReceived(data)
{
    alert("Data received");
    // Do your client side work here.
    // 'data' is an object containing the data sent from the web service 
    // Put a JS breakpoint on this line to explore the data object
}
43
NickG

ニックに感謝します。それは、私も最初にオンラインで見つけるのに苦労した問題に対する優れた回答でした。私にとってもうまくいきました。

この投稿の行が注目に値することを確認したかったのです。

組み込みのシリアライザー(System.Runtime.Serialization.Json)を使用したことを追加したかっただけで、魅力的にも機能しました。

        List<orderHistory> orderHistory = null;

        StringBuilder sb = new StringBuilder();
        JavaScriptSerializer js = new JavaScriptSerializer();
        sb.Append(callback + "(");
        sb.Append(js.Serialize(orderHistory));
        sb.Append(");");

        Context.Response.Clear();
        Context.Response.ContentType = "application/json";
        Context.Response.Write(sb.ToString());
        Context.Response.End();
3
Joe Ramos

誰かがASP.NETWeb APIアクションからJSONPを返す方法のサンプルを探している場合:

// GET api/values
public JsonpResult Get()
{
    var values = new string[] { "value1", "value2" };
    return new JsonpResult(values);
}

JsonpResultラップをカプセル化するJSONPヘルパークラス。

public class JsonpResult : JsonResult
{
    object _data = null;

    public JsonpResult(object data)
    {
        _data = data;
    }

    public override void ExecuteResult(ControllerContext controllerContext)
    {
        if (controllerContext != null)
        {
            var response = controllerContext.HttpContext.Response;
            var request = controllerContext.HttpContext.Request;

            var callBackFunction = request["callback"];
            if (string.IsNullOrEmpty(callBackFunction))
            {
                throw new Exception("Callback function name must be provided in the request!");
            }
            response.ContentType = "application/x-javascript";
            if (_data != null)
            {
                var serializer = new JavaScriptSerializer();
                response.Write(string.Format("{0}({1});", callBackFunction, serializer.Serialize(_data)));
            }
        }
    }
}
2
Dmitry Pavlov