XmlHttpRequest в закладке возвращает пустой responseText на GET? - PullRequest
4 голосов
/ 26 апреля 2010

Я пытаюсь создать букмарклет javascript для специальной службы сокращения URL, которую мы создали на http://esv.to для сокращения ссылок на Священные Писания (т. Е. "Матфея 5" становится "http://esv.to/Mt5". должен выполнить запрос GET к http://api.esv.to/Matthew+5,, который возвращает text/plain ответ http://esv.to/Mt5.

Код самого букмарклета выглядит следующим образом (расширен для удобства чтения):

var body = document.getElementsByTagName('body')[0], script = document.createElement('script');     
script.type = 'text/javascript';
script.src = 'http://esv.to/media/js/bookmarklet.js';
body.appendChild(script);
void(0);

Код от http://esv.to/media/js/bookmarklet.js выглядит так:

(function() {

    function shorten(ref, callback) {
      var url = "http://esv.to/api/" + escape(ref);
      var req = new XMLHttpRequest(); 
      req.onreadystatechange = function shortenIt() {
        if ( this.readyState == 4 && this.status == 200 ) {
          callback(req.responseText);
        };
      };
      req.open( "GET", url );
      req.send();
    };

    function doBookmarklet() {
      var ref = prompt("Enter a scripture reference or keyword search to link to:", "")
      shorten(ref, function (short) {
        prompt("Here is your shortened ESV URL:", short);
      });
    };

    doBookmarklet();

})();

При вызове из http://esv.to букмарклет работает правильно. Но при использовании на другой странице это не так. Странно то, что когда я смотрю запрос от Firebug, ответ равен 200 OK, браузер загружает 17 байтов (длина возвращаемой строки), но тело ответа пусто! Не выдается никакой ошибки, только пустой responseText в объекте XmlHttpRequest.

Теперь, согласно Ajax-вызову из Bookmarklet , GET не должен нарушать ту же политику происхождения. Это ошибка? Есть ли обходной путь?

1 Ответ

8 голосов
/ 28 апреля 2010

Межсайтовые XMLHttpRequests могут быть выполнены только в браузерах, которые реализуют спецификацию W3C Cross-Origin Resource Sharing и если сервер возвращает соответствующие заголовки контроля доступа (см. MDC статья ) Например:

Access-Control-Allow-Origin: *

Но это реализовано не всеми браузерами. Единственный надежный способ выполнения межсайтовых запросов - использовать JSONP , для (непроверенного) примера:

(function() {
    function shorten(ref, callback){
        var callbackFuncName = 'esvapiJSONPCallback' + (new Date()).valueOf();
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = "http://esv.to/api/" + escape(ref) + "?callback=" + callbackFuncName;
        window[callbackFuncName] = function(shorturl){
            script.parentNode.removeChild(script);
            window.callbackFuncName = null;
            delete window[callbackFuncName];
            callback(shorturl);
        };
        document.getElementsByTagName("head")[0].appendChild(script); 
    }

    var ref = prompt("Enter a scripture reference or keyword search to link to:", "");
    shorten(ref, function(shorturl) {
        prompt("Here is your shortened ESV URL:", shorturl);
    });
})();

Когда сервер видит параметр callback, он должен будет вернуть text/javascript вместо text/plain, и тело ответа необходимо будет заключить в вызов предоставленного обратного вызова, например:

<?php
#... after $shorturl is set ...
if(isset($_GET['callback'])){
    header('Content-Type: text/javascript');
    $callback = preg_replace('/\W+/', '', $_GET['callback']); #sanitize
    print $callback . "(" . json_encode($shorturl) . ");";
}
else {
    header("Content-Type: text/plain");
    print $shorturl;
}
?>
...