最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - XmlHttpRequest in a bookmarklet returns empty responseText on GET? - Stack Overflow

programmeradmin2浏览0评论

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

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 = '.js';
body.appendChild(script);
void(0);

The code from .js looks like this:

(function() {

    function shorten(ref, callback) {
      var url = "/" + 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 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?

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" bees "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?

Share Improve this question edited May 23, 2017 at 12:01 CommunityBot 11 silver badge asked Apr 26, 2010 at 17:46 David EykDavid Eyk 12.6k12 gold badges68 silver badges106 bronze badges 1
  • That SO answer that claims GET is not constrained by the Same Origin Policy is incorrect. Not sure why it got upvoted. – Crescent Fresh Commented Apr 28, 2010 at 18:12
Add a ment  | 

1 Answer 1

Reset to default 9 +100

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;
}
?>
发布评论

评论列表(0)

  1. 暂无评论