简体   繁体   English

如何控制HTML5 Web Worker上的XMLHttpRequest对象?

[英]How to control the XMLHttpRequest object on an HTML5 Web Worker?

I have a page which will normally overrides window.XMLHttpRequest with a wrapper that does a few extra things like inserting in headers on certain requests. 我有一个页面通常会覆盖window.XMLHttpRequest一个包装器,它会做一些额外的事情,比如在某些请求中插入标题。

I have some functionality in a 3rd party library that uses HTML5 Worker, and we are seeing that this request does not use the XMLHttpRequest wrapper object. 我在使用HTML5 Worker的第三方库中有一些功能,我们发现此请求不使用XMLHttpRequest包装器对象。 So any request that this library makes is missing the required headers, and so the request will fail. 因此,此库所做的任何请求都缺少必需的标头,因此请求将失败。

Is there a way to control the XMLHttpRequest that any Worker the current thread creates? 有没有办法控制当前线程创建的任何工作者的XMLHttpRequest?

This 3rd party library code looks like this: 此第三方库代码如下所示:

        function createWorker(url) {
            var worker = new Worker(url);
            worker.onmessage = function (e) {
                if (e.data.status) {
                    onprogress(e.data.status);
                } else if (e.data.error) {
                    onerror(e.data.error);
                } else {
                    exportUtils.saveFile(new Blob([e.data]), params.fileName);
                    onfinish();
                }
            };
            worker.postMessage(params); // window.location.origin +
            return worker;
        }

The Javascript that is returned by the URL variable above contains code like this: 上面的URL变量返回的Javascript包含如下代码:

        return new Promise(function(t, r) {
            var n = new XMLHttpRequest
              , a = "batch_" + o()
              , u = e.dataUrl.split(e.serviceUrl)[1]
              , c = [];
            n.onload = function() {
                for (var e = this.responseText, n = this.responseText.split("\r\n"), o = 0, a = n.length, i = a - 1; o < a && "{" !== n[o].slice(0, 1); )
                    o++;
                for (; i > 0 && "}" !== n[i].slice(-1); )
                    i--;
                n = n.slice(o, i + 1),
                e = n.join("\r\n");
                try {
                    var u = JSON.parse(e);
                    t(u)
                } catch (t) {
                    r(s + e)
                }
            }
            ,
            n.onerror = function() {
                r(i)
            }
            ,
            n.onabort = function() {
                r(i)
            }
            ,
            n.open("POST", e.serviceUrl + "$batch", !0),
            n.setRequestHeader("Accept", "multipart/mixed"),
            n.setRequestHeader("Content-Type", "multipart/mixed;boundary=" + a);
            for (var p in e.headers)
                "accept" != p.toLowerCase() && n.setRequestHeader(p, e.headers[p]);
            c.push("--" + a),
            c.push("Content-Type: application/http"),
            c.push("Content-Transfer-Encoding: binary"),
            c.push(""),
            c.push("GET " + u + " HTTP/1.1");
            for (var p in e.headers)
                c.push(p + ":" + e.headers[p]);
            c.push(""),
            c.push(""),
            c.push("--" + a + "--"),
            c.push(""),
            c = c.join("\r\n"),
            n.send(c)
        }
        )

The answer is both a soft "no" and an eventual "yes". 答案既是柔和的“不”,也是最终的“是”。

When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1). 当一段代码在不同的上下文(如webworker或iframe)中运行时,您无法直接控制其全局对象(1)。

What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api . 更重要的是,XMLHttpRequest并不是发送网络请求的唯一方法 - 你还有其他几种方法,其中主要是fetch api

However, there's a relatively new kid in block called Service Workers which can help you quite a bit! 然而,有一个相对较新的孩子叫做服务工作者,它可以帮助你很多!

Service workers 服务人员

Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. 服务工作者 (缩写为SW)与您已经知道的Web工作人员非常相似,但只要您的用户留在您的域中,他们就会继续在后台运行,而不是仅在当前页面中运行。 They are also global to your entire domain , so any request made from your site will be passed through them. 它们对您的整个域也是全球性的 ,因此从您的站点发出的任何请求都将通过它们传递。

Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses. 它们的主要目的是响应网络请求,通常用于缓存和离线内容,提供推送通知,以及其他一些小众用途。

Let's see a small example (note, run these from a local webserver): 让我们看一个小例子(注意,从本地网络服务器运行这些):

// index.html
<script>
navigator.serviceWorker.register('sw.js')
    .then(console.log.bind(console, 'SW registered!'))
    .catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
    fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
    console.log('fetch!', event);
})

Here we're registering a service worker and then requesting a page every 5 seconds. 在这里,我们注册一个服务工作者,然后每5秒请求一个页面。 In the service worker, we're simple logging each network event, which can be caught in the fetch event. 在服务工作者中,我们可以简单地记录每个网络事件,这些事件可以在fetch事件中捕获。

On first load, you should see the service worker being registered. 首次加载时,您应该看到正在注册的服务工作者。 SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. SW构成才开始拦截从它们安装的第一个页面请求......所以刷新页面,开始看到fetch被记录的事件。 I advise you to play around with the event properties before reading on so things will be clearer. 我建议你在阅读之前尽量使用事件属性,这样事情会更加清晰。

Cool! 凉! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. 我们可以通过在控制台中Request事件来看到event.request是我们的浏览器构造的Request对象。 In an ideal world, we could access event.request.headers and add our own headers! 在理想的世界中,我们可以访问event.request.headers并添加我们自己的标题! Dreamy, isn't it!? 梦幻,不是吗??

Unfortunately, request/response headers are guarded and immutable . 不幸的是, 请求/响应头是有保护的和不可变的 Fortunately, we are a stubborn bunch and can simply re-construct the request: 幸运的是,我们是一个顽固的群体,可以简单地重新构建请求:

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
    console.log('fetch!', event);
    // extract our request
    const { request } = event;

    // clone the current headers
    const newHeaders = new Headers();
    for (const [key, val] of request.headers) {
        newHeaders.append(key, val);
    }
    // ...and add one of our own
    newHeaders.append('Say-What', 'You heard me!');

    // clone the request, but override the headers with our own
    const superDuperReq = new Request(request, {
        headers: newHeaders
    });

    // now instead of the original request, our new request will take precedence!
    return fetch(superDuperReq);
});

This is a few different concepts at play so it's okay if it takes more than once to get. 这是一些不同的概念,所以如果它需要不止一次就可以。 Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! 基本上,我们正在创建一个新请求,它将代替原始请求发送,并设置一个新标头! Hurray! 欢呼!

在此输入图像描述

The Bad

Now, to some of the downsides: 现在,一些缺点:

  • Since we're hijacking every single request , we can accidentally change requests we didn't mean to and potentially destroy the entire universe! 由于我们正在劫持每一个请求 ,我们可能会意外地改变我们不想要的请求并可能破坏整个宇宙!
  • Upgrading SWs is a huge pain. 升级SW是一个巨大的痛苦。 SW lifecycle is complex, debugging it on your users is difficult. SW生命周期很复杂,难以对用户进行调试。 I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description 我已经看过一个关于处理它的好视频,遗憾的是现在找不到它,但是mdn有一个相当不错的描述
  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles 调试SW通常是一种非常烦人的体验,特别是当与他们奇怪的生命周期相结合时
  • Because they are so powerful, SWs can only be served over https. 因为它们非常强大,SW只能通过https提供。 You should already be using https anyway, but this is still a hindrance 你应该已经在使用https,但这仍然是一个障碍
  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity 这对于相对较小的好处来说有很多事情要做,因此可能会重新考虑它的必要性

(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed. (1)你可以访问一个iframe的全局对象在同一产地如你,但让你的代码运行修改全局对象是棘手的确实。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM