Прототип AJAX-запроса отправляется как ОПЦИИ, а не GET; приводит к ошибке 501
Я пытаюсь получить доступ к веб-сервису с помощью Prototype/AJAX и запутался в ошибке, которую я не могу понять: кажется, что когда я делаю запрос на сервер, мой запрос интерпретируется как ОПЦИИ, а не GET (и, в свою очередь, выдает ошибку 501 - не реализована, поскольку сервер разрешает только запросы GET, исходя из того, что я понимаю из Access-Control-Request-Method:
). Я что-то пропустил в своей формулировке AJAX/request, которая может вызвать эту ошибку? Я читал немного в запросах CORS/preflighted здесь, но я не уверен, как это можно применить, когда мой код выглядит совместимым...
Здесь соответствующий запрос 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:
, являются ли они исключительно единственными заголовками, разрешенными в запросе?
Спасибо!
Ответы
Ответ 1
Слишком много часов ищет правильное исправление на prototypejs... наконец, у нас есть неинтрузивное решение на отличная статья (Wilson Lee)!. Вот выдержка:
Большинство основных фреймворков 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;
});
}
});
Если вы видите какой-либо недостаток/улучшение этого решения, мы будем рады вам поделиться:)
Ответ 2
Фактически это предпросмотр запроса, потому что Prototype добавляет пользовательские заголовки X-Requested-With, X-Prototype-Version к запросу. Из-за этих заголовков браузер отправляет первый запрос OPTIONS
. Спецификация XHR говорит:
Для запросов одного и того же происхождения, использующих метод HTTP GET, запрашивается предполетный запрос, когда установлены заголовки, отличные от Accept и Accept-Language.
Как решить эту проблему? Я вижу только одну возможность решить эту проблему как можно скорее: полностью перезаписать метод Ajax.Request#setRequestHeaders()
, например. вставьте этот script сразу после 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()
(здесь я пропущу этот вариант, чтобы сделать ответ короче).
Ответ 3
Собственно, это намного проще с Prototype.js V1.7:
Ajax.Responders.register({
onCreate:function(r){
r.options.requestHeaders={
'X-Prototype-Version':null,
'X-Requested-With':null
};
}
});
Prototype.js удаляет любой предварительно определенный заголовок, если его значение равно null.
Ответ 4
Я никогда не использовал Prototype, и я не уверен, насколько я буду использовать. Но я быстро просмотрел документы, и я не видел поддержки методов и параметров.
Итак, попробуйте:
new Ajax.Request(REQUEST_ADDRESS+"?stationString="+station_id, {
onSuccess: displayMetar,
onFailure: function() {
$("errors").update("an error occurred");
}
});
Также я заметил, что stationString в вашем примере должна быть в кавычках, предполагая, что это не переменная.