Подтвердить что ты не робот

Как я могу создать JSONP из веб-службы ASP.NET для междоменных вызовов?

Я написал веб-сервис, который возвращает JSON, и я попытался вызвать его с помощью jQuery следующим образом:

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

Однако код никогда не вызывает функцию успеха, несмотря на успешность вызова webservice при просмотре HTTP-трафика с использованием Fiddler. Я думаю, это связано с тем, что мой веб-сервис возвращает сырой JSON вместо JSONP.

Как я могу создать JSONP в качестве ответа от стандартного метода .NET webservice, например:

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

Спасибо.

4b9b3361

Ответ 1

Хорошо, я в конце концов понял это сам. Поскольку мне так трудно найти полное рабочее решение в Интернете, я решил документировать свое рабочее решение здесь.

Ответ JSONP - это стандартная строка JSON, заключенная в вызов функции. ASP.NET, похоже, не дает возможности вернуть ответ в этом формате напрямую, но это очень просто сделать сами. Однако вы должны переопределить метод JSON по умолчанию.

Ниже приведен пример JSONP.

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

.. теперь этот бит: { name: 'value';} является стандартным JSON, который предоставит вам любой последовательный сериализатор JSON, поэтому все, что нам нужно сделать, - это привязка к оболочке вызова функций. К сожалению, это означает, что мы должны "освободить" (или обойти) существующую кодировку JSON, которая прозрачно обрабатывается каркасом при возврате объекта из функции веб-службы.

Это делается путем полного перекрытия ответа от функции веб-службы путем записи JSONP в выходной поток (Response) с использованием нашего собственного кода. Это на самом деле довольно просто, и я привел пример ниже.

Вы можете использовать встроенный DataContractJsonSerializer (из пространства имен System.Runtime.Serialization.Json в ASP.NET 3.5+) или NewtonSoft JSON, и оба примера показаны ниже. Я предпочитаю использовать NewtonSoft JSON (установленный из nuget), а не встроенный JSON-сериализатор, поскольку я нахожу, что он дает вам больше контроля, а также может выводить хорошо отформатированные для человека 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
}

Ответ 2

Спасибо, Ник, это был отличный ответ на проблему, которую мне тоже нелегко было найти в Интернете. Отлично работал и для меня.

Хотелось убедиться, что эта строка сообщения получила внимание, которого она заслуживает.

Просто хотел добавить, что я использовал встроенный сериализатор (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

Если кто-то ищет образец, как вернуть JSONP из ASP.NET Web API action:

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