8

Prototype/AJAX を使用して Web サービスにアクセスしようとしましたが、エラーが発生してわかりません: サーバーにリクエストを送信すると、リクエストが GET リクエストではなく OPTIONS として解釈されるようです (サーバーはGETリクエストのみを許可するため、私が理解していることに基づいて、501 -実装されていないエラーがスローされます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

ここで何を見落とすことができますか?リクエストが GET ではなく OPTIONS として送信されていると Chrome が言うのはなぜですか? Chromeが情報を吐き出すときAccess-Control-Request-Headers:、これらはリクエストで許可されている唯一のヘッダーですか?

ありがとう!

4

4 に答える 4

15

プロトタイプjsの正しい修正を探すのに時間がかかりすぎます...最後に、素晴らしいkourge(Wilson Lee)の記事に邪魔にならない解決策があります!。抜粋は次のとおりです。

ほとんどの主要なAjaxフレームワークは、インスタンス化するAjaxリクエストにカスタムHTTPヘッダーを設定するのが好きです。最も人気のあるヘッダーは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; 
                }); 
            } 
        });

このソリューションに不利な点/改善が見られる場合は、共有することを歓迎します:)

于 2013-03-08T17:39:57.350 に答える
6

Prototype はカスタム ヘッダーX-Requested-With、X-Prototype-Versionをリクエストに追加するため、実際にはpreflight requestです。これらのヘッダーにより、ブラウザは最初のリクエストを送信します。XHR仕様は次のように述べています:OPTIONS

HTTP GET メソッドを使用した同一オリジンでないリクエストの場合、Accept および Accept-Language 以外のヘッダーが設定されている場合、プリフライト リクエストが作成されます。

この問題を解決するには?これをできるだけ早く解決する可能性は 1 つしかないと思います。メソッド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()(回答を短くするために、ここではこのバリアントをスキップします)。

于 2012-12-11T19:24:20.980 に答える
1

実際、Prototype.js V1.7 を使用すると、はるかに簡単になります。

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

Prototype.js は、値が null の場合、定義済みのヘッダーを削除します。

于 2015-09-02T10:48:18.673 に答える
0

私は Prototype を使ったことがないので、どれだけ使えるかわかりません。しかし、ドキュメントをざっと見たところ、メソッドとパラメーターのサポートは見当たりませんでした。

だから試してください:

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

また、あなたの例の stationString は、変数ではないと仮定して引用符で囲む必要があることに気付きました。

于 2012-12-11T06:29:16.950 に答える