Ajax, jQuery и JSONP

#ajax #json #jquery

#ajax #json #jquery

Вопрос:

Похоже, что я могу получить данные JSON из другого домена, используя метод getJSON от jQuery (см.:http://docs.jquery.com/Getjson). Однако это работает только для HTTP GET.

Что, если бы мне нужно было что-то опубликовать и получить ответ в формате JSON? Как бы я это сделал в jQuery / Ajax?

Комментарии:

1. tvanfosson был очень полезен мне — и, надеюсь, другим — прокомментировав мой ответ. Я был бы признателен, если бы вы подумали о том, чтобы отдать ему должное за ответ, за который он получил бы мою награду в 50 баллов.

Ответ №1:

Начиная с версии 1.6.1, во всех браузерах невозможно ОТПРАВЛЯТЬ запросы на удаленный сервер от клиента, используя только jQuery. Если вы попытаетесь отправить XHttpRequest любого рода на сервер в домене, отличном от документа, некоторые браузеры просто не смогут его выполнить. Запросы JSONP к удаленным серверам обрабатываются путем создания тега script, src для которого является URL API с добавленными параметрами запроса, включая имя метода обратного вызова. Поскольку скрипты могут быть загружены из любого домена, это работает, но ограничивает вас в ПОЛУЧЕНИИ запросов. Удаленный хост возвращает тело скрипта, который является обратным вызовом, вызываемым для результирующего объекта javascript. jQuery обычно создает для вас функцию обратного вызова и из нее вызывает функцию анонимного обратного вызова, которую вы указываете в getJSON параметрах метода.

Появляются новые стандарты, CORS и UMP (см. Также Сравнение), которые поддерживают некоторые браузеры, но не стандартизированными способами (читайте, IE делает это по-другому). Существуют плагины для обеспечения частичной поддержки тех браузеров, которые поддерживают CORS. Понятия не имею, насколько хорошо они работают, и они не будут работать, если браузер не поддерживает это.

Комментарии:

1. Постер упомянул в комментарии, что он / она также несет ответственность за создание API. Если они реализуют требуемый протокол (что вы можете легко сделать, если вы уже знаете, как создавать RESTful services), то это возможно. Смотрите мой другой ответ.

2. @Steve — мой ответ специфичен для jQuery, который не поддерживает элементарные реализации CORS в тех браузерах, в которых он есть. Существует едва поддерживаемый плагин, но, похоже, он не находится в активной разработке. Возможно, это может измениться, но из коробки нет способа сделать это с помощью jQuery.

3. На самом деле, это так, поскольку он использует XMLHttpRequest в фоновом режиме … который автоматически выполнит предварительный запрос параметров. Я использую эту функцию ежедневно и могу подтвердить, что она работает, как описано в спецификации W3C, во всех основных браузерах.

Ответ №2:

Другие ответы не совсем верны. Это возможно, если у вас есть контроль над сервером.

Смотрите: W3C — Совместное использование ресурсов из разных источниковhttp://www.w3.org/TR/cors

По сути, клиент отправляет HTTP-запрос с «предполетными» ПАРАМЕТРАМИ, и, если от сервера получен правильный ответ, он продолжает выполнять свои обычные операции. (В Интернете есть множество примеров… Если вам это не нужно, я не буду вдаваться в подробности).

Я понимаю, что это может работать не во всех сценариях (например, я не уверен, поддерживает ли IE5 / 5.5 это или нет… но я полагаю, что IE6 делает)… но если вы работаете над приложением HTML5 и у вас есть контроль над сервером, это может стать для вас возможным.

ПРИМЕЧАНИЕ: Просто в сторону — учитывая вариант, я бы предпочел JSONP, конечно. Меньше ошибок.

РЕДАКТИРОВАТЬ: Похоже, здесь много путаницы, поэтому позвольте мне привести пример того, как это можно сделать с помощью .NET / WCF (я думаю, что кое-что из этого взято из какой-то статьи, а другие ее части были разработаны собственными силами… так что, если что-то из этого пришло откуда-то еще, я заранее приношу извинения за то, что не отдал должного должное):

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YourNamespaceHere
{
    using System;
    using System.Web; 
    using System.Collections;

    public class CrossOriginModule : IHttpModule {
        public String ModuleName {
            get { return "CrossOriginModule"; } 
        }    

        public void Init(HttpApplication application) {
            application.BeginRequest  = (new EventHandler(this.Application_BeginRequest));
        }

        private void Application_BeginRequest(Object source, EventArgs e) {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            CrossOriginHandler.SetAllowCrossSiteRequestOrigin(context);
        }

        public void Dispose() 
        {
        }
    }

   public class CrossOriginHandler : IHttpHandler
    {
        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            //Clear the response (just in case)
            ClearResponse(context);

            //Checking the method
            switch (context.Request.HttpMethod.ToUpper())
            {
                //Cross-Origin preflight request
                case "OPTIONS":
                    //Set allowed method and headers
                    SetAllowCrossSiteRequestHeaders(context);
                    //Set allowed origin
                    //This happens for us with our module:
                    SetAllowCrossSiteRequestOrigin(context);
                    //End
                    context.Response.End();
                    break;

                default:
                    context.Response.Headers.Add("Allow", "OPTIONS");
                    context.Response.StatusCode = 405;
                    break;
            }

            context.ApplicationInstance.CompleteRequest();
        }
        #endregion

        #region Methods
        protected void ClearResponse(HttpContext context)
        {
            context.Response.ClearHeaders();
            context.Response.ClearContent();
            context.Response.Clear();
        }

        protected void SetNoCacheHeaders(HttpContext context)
        {
            context.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
            context.Response.Cache.SetValidUntilExpires(false);
            context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.Cache.SetNoStore();
        }
        #endregion

        public static void SetAllowCrossSiteRequestHeaders(HttpContext context)
        {
            string requestMethod = context.Request.Headers["Access-Control-Request-Method"];

            context.Response.AppendHeader("Access-Control-Allow-Methods", "GET,POST");

            //We allow any custom headers
            string requestHeaders = context.Request.Headers["Access-Control-Request-Headers"];
            if (!String.IsNullOrEmpty(requestHeaders))
                context.Response.AppendHeader("Access-Control-Allow-Headers", requestHeaders);
        }

        public static void SetAllowCrossSiteRequestOrigin(HttpContext context)
        {
            string origin = context.Request.Headers["Origin"];
            if (!String.IsNullOrEmpty(origin))
                context.Response.AppendHeader("Access-Control-Allow-Origin", origin);
            else
                //This is necessary for Chrome/Safari actual request
                context.Response.AppendHeader("Access-Control-Allow-Origin", "*");
        }
    }
}
  

И в Web.config:

   ...
  <system.webServer>
     ...
     <modules runAllManagedModulesForAllRequests="true">
     ...
           <add name="CrossOriginModule" preCondition="managedHandler" type="YOURNANMESPACEHERE.CrossOriginModule, ASSEMBLYNAME" />
    </modules>
    <handlers>
           <add name="CrossOrigin" verb="OPTIONS" path="*" type="YOURNAMESPACEHERE.CrossOriginHandler, ASSEMBLYNAME" />
    </handlers>
  </system.webServer>
  

Комментарии:

1. «Учитывая вариант, я бы предпочел JSONP» — вы понимаете, что JSONP существует только из-за той же политики происхождения? Если бы XMLHttpRequest можно было использовать для выполнения HTTP-запросов между доменами, просто не было бы необходимости в JSONP, который является обходным путем.

2. Согласно моему ответу, XMLHttpRequest может использоваться для междоменных запросов. Я делаю это постоянно. Но JSONP более надежен в том смысле, что он работает в гораздо более широком диапазоне сценариев … вот почему я бы предпочел его. Обходной путь или нет.

3. Похоже, что в jQuery для этого мало поддержки — то, что существует, является лишь частичным и зависит от браузера (например, нет поддержки Opera). OP, похоже, хотел решение на jQuery, которое в настоящее время невозможно из коробки, по крайней мере, в полном спектре браузеров.

4. @tvanfosson: На самом деле, это так, поскольку он использует XMLHttpRequest в фоновом режиме … который автоматически выполнит предварительный запрос параметров. Я использую эту функцию ежедневно и могу подтвердить, что она работает, как описано в спецификации W3C, во всех основных браузерах. Однако я не могу поручиться за Opera в частности… Я уверен только на 100% в IE 6 , Safari, Chrome и Firefox.

5. @Steve IE8 использует XDocumentRequest, а не XMLHttpRequest, для поддержки CORS, насколько я понимаю. Я думаю, вам нужно было бы добавить кое-что в jQuery для поддержки современных версий IE. Вы уверены, что не добавили плагин CORS?

Ответ №3:

вкратце: JsonP — это междоменный метод, ограниченный запросом GET.

Ответ №4:

  1. test.php
 <?php   fire query
        $row = array();
        while($queryresults){
          $row['id'] = '$queryresults['idfield']';
          $row['name'] = '$queryresults['namefield']';
          $row['marks'] = '$queryresults['marksfield']';
         $output[] = $row;
         }
echo json_encode( $output ); //json array
?>
  
  1. документ готов
 $.getJSON('test.php?query=query,function(data) {    
    $.each(enq_data, function(i,data){
          $('.anydiv').append('<div class="row">' data.id data.name data.marks '</div>');
    });
});