Прототип AJAX-запроса отправляется как ОПЦИИ, а не как GET; приводит к ошибке 501

Я пытаюсь получить доступ к веб-сервису с помощью Prototype/AJAX и сталкиваюсь с ошибкой, которую не могу понять: кажется, что когда я делаю запрос к серверу, мой запрос интерпретируется как OPTIONS, а не как запрос GET (и в свою очередь выдает ошибку 501 - не реализовано, так как сервер разрешает только запросы GET, основываясь на том, что я понимаю из Access-Control-Request-Method:). Я что-то упускаю в своей формулировке AJAX/ запроса, которая может быть причиной этой ошибки? Я прочитал немного в CORS/ предварительные запросы здесь, но я не уверен, как это может применяться, когда мой код выглядит совместимым...

Вот соответствующий запрос AJAX:

function fetchMetar() {
var station_id = $("station_input").value;

    new Ajax.Request(REQUEST_ADDRESS, {
        method: "get",
        parameters: {stationString: station_id},
        onSuccess: displayMetar,
        onFailure: function() {
            $("errors").update("an error occurred");
        }
    });
}

и вот ошибка и соответствующая информация запроса, которую я получаю от Chrome:

Request URL:http://weather.aero/dataserver_current/httpparam?
 dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3
 &mostRecent=true&stationString=&stationString=KSBA
Request Method:OPTIONS
Status Code:501 Not Implemented
Request Headers
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:origin, x-prototype-version, x-requested-with, accept
Access-Control-Request-Method:GET
Connection:keep-alive
Host:weather.aero
Origin:http://domain.com
Referer:http://domain.com/.../...html

Что я мог не заметить здесь? Почему Chrome говорит, что запрос отправляется как ОПЦИИ, а не как GET? Когда Chrome выплевывает Access-Control-Request-Headers: информация, это только единственные заголовки, разрешенные в запросе?

Спасибо!

4 ответа

Слишком много часов в поисках правильного решения для prototypejs... наконец, у нас есть неинтрузивное решение для статьи о великом Курге (Уилсон Ли)! Вот выдержка:

Большинство основных Ajax-инфраструктур предпочитают устанавливать пользовательские HTTP-заголовки в Ajax-запросах, которые вы создаете; самый популярный заголовок - X-Requested-With: XMLHttpRequest. Следовательно, ваш запрос повышен до предварительно выданного и терпит неудачу. Исправление состоит в том, чтобы запретить вашей платформе JavaScript устанавливать эти пользовательские заголовки, если ваш запрос является междоменным. jQuery уже удачно избегает непреднамеренного предварительного запроса, не устанавливая пользовательские заголовки, если ваш URL считается удаленным. Вы должны были бы вручную предотвратить это, если вы используете другие фреймворки.

Это может быть так просто, как:

new Ajax.Request('http://www.external-domain.net/my_api.php?getParameterKey=getParameterValue', {
            method:'post',
            contentType:"application/x-www-form-urlencoded",
            postBody:'key=' + value,
            onSuccess: function(response) {
                // process response
            },
            onCreate: function(response) { // here comes the fix
                var t = response.transport; 
                t.setRequestHeader = t.setRequestHeader.wrap(function(original, k, v) { 
                    if (/^(accept|accept-language|content-language)$/i.test(k)) 
                        return original(k, v); 
                    if (/^content-type$/i.test(k) && 
                        /^(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)(;.+)?$/i.test(v)) 
                        return original(k, v); 
                    return; 
                }); 
            } 
        });

Если вы видите какие-либо недостатки / улучшения этого решения, мы приглашаем вас поделиться:)

Фактически это предварительный запрос, потому что Prototype добавляет в запрос пользовательские заголовки X-Requested-With, X-Prototype-Version. Из-за этих заголовков браузер отправляет первым OPTIONS запрос. Спецификация XHR гласит:

Для не совпадающих исходных запросов с использованием метода HTTP GET предварительный запрос выполняется, когда установлены заголовки, отличные от Accept и Accept-Language.

Как решить эту проблему? Я вижу только одну возможность решить эту проблему как можно скорее: полностью перезаписать метод Ajax.Request#setRequestHeaders() Например, вставьте этот скрипт сразу после Prototype.js:

Ajax.Request.prototype.setRequestHeaders = function() {
  var headers = {
    // These two custom headers cause preflight request:
    //'X-Requested-With': 'XMLHttpRequest',
    //'X-Prototype-Version': Prototype.Version,
    'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
  };

  if (this.method == 'post') {
    headers['Content-Type'] = this.options.contentType +
      (this.options.encoding ? '; charset=' + this.options.encoding : '');

    /* Force "Connection: close" for older Mozilla browsers to work
     * around a bug where XMLHttpRequest sends an incorrect
     * Content-length header. See Mozilla Bugzilla #246651.
     */
    if (this.transport.overrideMimeType &&
        (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
          headers['Connection'] = 'close';
  }

  if (typeof this.options.requestHeaders == 'object') {
    var extras = this.options.requestHeaders;

    if (Object.isFunction(extras.push))
      for (var i = 0, length = extras.length; i < length; i += 2)
        headers[extras[i]] = extras[i+1];
    else
      $H(extras).each(function(pair) { headers[pair.key] = pair.value; });
  }

  for (var name in headers)
    this.transport.setRequestHeader(name, headers[name]);
}

Этот патч удаляет пользовательские заголовки из любого запроса AJAX. В случае, когда вам все еще нужны эти заголовки для запросов не-CORS, может быть добавлено больше логики, что даст возможность отключить эти заголовки в опциях для new Ajax.Request() (Я пропущу этот вариант здесь, чтобы сделать ответ короче).

На самом деле, с Prototype.js V1.7 это намного проще:

Ajax.Responders.register({
    onCreate:function(r){
        r.options.requestHeaders={
        'X-Prototype-Version':null,
        'X-Requested-With':null
        };
    }
});

Prototype.js удаляет любой предопределенный заголовок, если его значение равно нулю.

Я никогда не использовал Prototype, и я не уверен, насколько я буду его использовать. Но я быстро просмотрел документы и не увидел никакой поддержки метода и параметров.

Так что постарайтесь:

new Ajax.Request(REQUEST_ADDRESS+"?stationString="+station_id, {
    onSuccess: displayMetar,
    onFailure: function() {
        $("errors").update("an error occurred");
    }
});

Также я только что заметил, что stationString в вашем примере должен быть в кавычках, предполагая, что это не переменная.

Другие вопросы по тегам