【问题标题】:jQuery: access binary AJAX response inside complete function or access XHR object outside of callback functions?jQuery:在完整函数内访问二进制 AJAX 响应还是在回调函数外访问 XHR 对象?
【发布时间】:2021-09-08 15:37:18
【问题描述】:

关于使用 jQuery 处理二进制 AJAX 响应有很多问题,例如 thisthisthis。没有任何帮助。

目标:(1)动态判断响应是否包含二进制数据,并以不同于文本的方式处理; (2) 分析响应头;和 (3) 从一个函数中完成。

理想情况下,这可以通过 complete 函数完成,但 complete 函数无权访问 XHR 对象。 jqXHR 应该是 XHR 对象的超集,但 XHR.response 是空白的。

$.ajax(settings) 的返回值包含二进制数据,但 XHR 对象不再可用——因此似乎无法分析响应标头。

是否可以在 complete 回调中访问二进制 AJAX 响应或在回调函数之外访问 XHR 对象?

// Assume @data contains body and URL values.

let settings = {
    url: data.url,
    method: "post",
    timeout: 0,
    contentType: false,
    processData: false,
    data: data.body,
    xhr: function() {
                // Create XMLHttpRequest object.
                let xhr = new XMLHttpRequest();

                // Handle event for when response headers arrive.
                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 2) {
                        if (xhr.status == 200) {
                            xhr.responseType = 'blob';
                        } else {
                            xhr.responseType = 'text';
                        }
                    }
                };

                return xhr;
    },
    complete: function(xhr, status, error) {
        // Can access response headers but not binary response here.
    }
};

let response = await $.ajax(settings);
// @response is blob but cannot access XHR object here.

【问题讨论】:

    标签: javascript jquery ajax


    【解决方案1】:

    我假设您使用的是 jQuery v1.5+。 complete 回调已被弃用,取而代之的是 always,其中包括响应正文。我可以在一个函数中获取它以及响应标头,执行如下操作:

    $.ajax({
        url: 'https://static.wikia.nocookie.net/5dabc4c4-7cdb-438d-ba57-6b09afffcbb4',
        method: 'get',
    })
      .always((responseBody, responseStatus, xhr) => {
        // You don't need this check when using .done()
        if (responseStatus === 'success') {
          if (xhr.getResponseHeader('content-type').startsWith('image')) {
            processImage(responseBody)
          }
        }
      })
    

    这也应该适用于您的示例。让我知道这是否有效。

    【讨论】:

    • 感谢您的回答。我们如何更改问题中描述的响应类型?问题是二进制数据被 jquery 视为文本。
    【解决方案2】:

    我不知道你是否被一些古老的 jQuery 版本所限制,但实际上你可以做到所有这些。

    $.ajax(settings) 的返回值包含二进制数据,但 XHR 对象不再可用——因此似乎无法分析响应标头。

    技术上,不。 $.ajax(settings) 的返回值是 jqXHR 对象,因此您可以访问在回调中获得的相同对象。 在您的代码中,您使用的是 async/await 模式,因此无法访问 jqXHR 对象。但你可以这样做

    let jqXHR = $.ajax(settings);
    jqXHR.then((response) => {
        console.log(response);
        console.log(res.getAllResponseHeaders());
    });
    

    这样,您将同时获得 jqXHR 对象和响应对象。

    理想情况下,这可以通过完整函数完成,但完整函数无权访问 XHR 对象。 jqXHR 应该是 XHR 对象的超集,但是 XHR.response 是空白的。

    因为您在错误的时间访问它。为了填充response 属性,您必须等到readyState 4 而不是2。请参阅XHR readyState
    再说一次,您也可以在完整的回调中获取响应对象。 只需像处理 $.ajax(settings) 响应一样处理 xhr 回调参数即可。

    complete: function(xhr, status, error) {
        res.then((response) => {
            console.log(response);
            console.log(res.getAllResponseHeaders());
        });
    }
    

    如果您使用的是较新版本的 jQuery(我猜是最近 2-3 年的版本),您甚至可以编写这个简洁的代码

    complete: async function (xhr, status, error) {
        console.log(xhr.getAllResponseHeaders());
        console.log(await xhr);
    }
    

    如果你确实需要 XHR 对象,你也可以这样做。

    let xhr;
    let settings = {
        //rest of things
        xhr: function () {
            return xhr = new XMLHttpRequest();
        }
    };
    let jqXHR = $.ajax(settings);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 2) {
            if (xhr.status == 200) {
                xhr.responseType = 'blob';
            } else {
                xhr.responseType = 'text';
            }
        }
    };
    let response = await jqXHR;
    console.log(response);
    

    【讨论】:

    • 感谢您的回答。使用较新的方法,我们如何更改问题中描述的响应类型?问题是二进制数据被 jquery 视为文本。
    • 为什么使用 async/await 和回调不同?返回值不应该一样吗?
    • 据我所知,没有任何更新的方法可以更改响应类型。您仍然需要以某种方式设置 responseType 属性。你目前的方法可以解决这个问题。
    • 使用直接 async/await,您无法访问原始的 Promise 对象。你得到的是传递给解决 Promise 回调的 value 参考developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 使用回调方法,你可以访问两者。如果您想要 async/await 方法和 promise 对象,请参考最后一个示例。 javascipt let jqXHR = $.ajax(settings); //promise object let response = await jqXHR; //async/await to get the response //do whatever you want with the jqXHR object, like getting headers
    【解决方案3】:

    为了动态确定响应,使用success 函数而不是alwayscomplete 更方便(这些也会在出错时运行)。在其中,您将获得jqXHR 对象,并且您可以读取发回的数据类型。在确定它是二进制后(在下面的代码中,它是image/png),您可以将响应从文本转换为二进制,并可选择将其放入Blob。像这样:

    let data = {
        url: "https://www.gravatar.com/avatar/cc0c5253923613abd592330ed8adb3a0?s=48&d=identicon&r=PG",
        body: {}
    }
    let settings = {
        url: data.url,
        method: "post",
        timeout: 0,
        contentType: false,
        processData: false,
        data: data.body,
        success: function(response, status, jqXHR) {
            var type = jqXHR.getResponseHeader('content-type');
            console.log(response);
            console.log(type);
            if(type === 'image/png'){
                var output = [];
                for (var i = 0; i < response.length; i++) {
                      output.push( response[i].charCodeAt(0).toString(2) + " ");
                }
                output = new Blob(output, {type : type});
                console.log(output);
            }
            else{
                console.log('type is not image/png');
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.log("an error was encountered", errorThrown);
        }
    };
    
    $.ajax(settings);
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-05
      • 1970-01-01
      • 2011-07-25
      • 2011-07-07
      • 1970-01-01
      • 2013-01-19
      相关资源
      最近更新 更多