原型AJAX请求作为OPTIONS而不是GET发送; 导致501错误

Phi*_*vid 8 javascript ajax prototypejs http-headers

我正在尝试使用Prototype/AJAX访问Web服务并遇到一个我无法弄清楚的错误:似乎当我向服务器发出请求时,我的请求被解释为OPTIONS而不是GET请求(反过来抛出一个501 - 未实现的错误,因为服务器只根据我的理解允许GET请求Access-Control-Request-Method:).我在AJAX /请求公式中遗漏了可能导致此错误的内容吗?我在这里阅读了一些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");
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

这是我从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
Run Code Online (Sandbox Code Playgroud)

我能在这里俯瞰什么?为什么Chrome会将请求作为OPTIONS而不是GET发送?当Chrome吐出Access-Control-Request-Headers:信息时,这些信息是否仅是请求中允许的唯一标头?

谢谢!

Kat*_*ico 15

太多时间寻找对prototypejs的正确修复......最后,我们在伟大的kourge(Wilson Lee)文章中有一个非侵入性的解决方案!这是一段摘录:

大多数主要的Ajax框架都喜欢在Ajax上设置自定义HTTP头请求你实例化; 最受欢迎的标题是X-Requested-With:XMLHttpRequest.因此,您的请求将被提升为预检请求并失败.该修复是为了防止您的JavaScript框架在您的请求是跨域请求时设置这些自定义标头.如果您的URL被认为是远程的,jQuery已经巧妙地避免了因为没有设置自定义标头而无意中预检请求.如果您正在使用其他框架,则必须手动阻止此操作.

它可以很简单:

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; 
                }); 
            } 
        });
Run Code Online (Sandbox Code Playgroud)

如果您发现此解决方案有任何不利/改进,欢迎您分享:)


Vic*_*tor 6

实际上它是预检请求,因为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]);
}
Run Code Online (Sandbox Code Playgroud)

此修补程序从任何AJAX请求中删除自定义标头.如果您仍然需要这些标头用于非CORS请求,可能会添加更多逻辑,这将有可能在选项中禁用这些标头new Ajax.Request()(我将在此处跳过此变体以使答案更短).


小智 0

我从未使用过 Prototype,而且我不确定它会有多大用处。但我快速浏览了文档,没有看到任何对方法和参数的支持。

所以尝试:

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

另外,我刚刚注意到您的示例中的 stationString 应该用引号引起来,假设它不是变量。