小书签中的 XmlHttpRequest 在 GET 上返回空响应文本?

发布于 2024-08-30 11:48:04 字数 1787 浏览 10 评论 0 原文

我正在尝试为我们在 http://esv.to 用于缩短经文引用(即“马太福音 5”变为“http://esv.to/Mt5”。小书签应该对 http://api.esv.to/Matthew+5< 执行 GET 请求/a>,它返回 http://esv.to/Mt5text/plain 响应。

书签本身的代码如下所示(为了便于阅读而进行了扩展)。 :

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个字节(返回字符串的长度),但响应正文为空!不会引发任何错误,只是 XmlHttpRequest 对象上出现一个空的responseText。

现在,根据 来自 Bookmarklet 的 Ajax 调用,GET 不应违反同源策略。这是一个错误吗?有解决方法吗?

I'm trying to build a javascript bookmarklet for a special URL shortening service we've built at http://esv.to for shortening scripture references (i.e. "Matthew 5" becomes "http://esv.to/Mt5". The bookmarklet is supposed to do a GET request to http://api.esv.to/Matthew+5, which returns a text/plain response of http://esv.to/Mt5.

The code for the bookmarklet itself looks like this (expanded for readability):

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);

The code from http://esv.to/media/js/bookmarklet.js looks like this:

(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();

})();

When called from http://esv.to itself, the bookmarklet works correctly. But when used on another page, it does not. The strange thing is, when I watch the request from Firebug, the response is 200 OK, the browser downloads 17 bytes (the length of the returned string), but the response body is empty! No error is thrown, just an empty responseText on the XmlHttpRequest object.

Now, according to Ajax call from Bookmarklet, GET shouldn't violate the same origin policy. Is this a bug? Is there a workaround?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

酒与心事 2024-09-06 11:48:04

跨站点 XMLHttpRequest 只能在实现 W3C 跨源资源共享 规范的浏览器中完成如果服务器返回适当的访问控制标头(请参阅 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,并且响应body 需要包装在所提供回调的调用中,例如:

<?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;
}
?>

Cross-site XMLHttpRequests can only be done in browsers that implement the W3C Cross-Origin Resource Sharing spec and if the server returns the appropriate access control headers (see MDC article), e.g.:

Access-Control-Allow-Origin: *

But this is not implemented by all browsers. The only sure-fire way to do cross-site requests is to use JSONP, for (untested) example:

(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);
    });
})();

When the server sees the callback parameter it would then need to return text/javascript instead of text/plain, and the response body would need to be wrapped in an invocation of the provided callback, for example:

<?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;
}
?>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文