Navigator.sendBeacon() 传递 header 信息

Navigator.sendBeacon() to pass header information

我正在使用 navigator 与服务器通信,但问题是我们需要传递一些 header 信息,因为存在识别请求来自有效来源的过滤器。

有人可以帮忙吗?

谢谢。

在搜索这个问题的答案后,我发现要通过导航器传递 header,我们需要传递一个 blob object。

例如

var headers = {type: 'application/json'};
var blob = new Blob(request, headers);
navigator.sendBeacon('url/to/send', blob);

首先,请注意并非所有浏览器都支持 navigator.sendBeacon。在 MDN documentation.

查看有关此功能以及当前支持的浏览器的更多详细信息

您确实创建了一个 blob 来提供 headers。这是一个例子:

window.onunload = function () {
  const body = {
    id,
    email,
  };
  const headers = {
    type: 'application/json',
  };
  const blob = new Blob([JSON.stringify(body)], headers);
  navigator.sendBeacon('url', blob);
});

navigator.sendBeacon 将发送一个 POST 请求,其中 Content-Type 请求 header 设置为 headers.type 中的任何内容。根据 W3C:

,这似乎是您唯一可以在信标中设置的 header

The sendBeacon method does not provide ability to customize the request method, provide custom request headers, or change other processing properties of the request and response. Applications that require non-default settings for such requests should use the [FETCH] API with keepalive flag set to true.

我能够通过这个 Chromium bug report.

观察到它是如何工作的

Processing Model of sendBeacon中所写:

Extract object's byte stream (transmittedData) and content type (contentType).

如何提取是described here

我收集到的是提取传输数据的内容类型,并将其设置为HTTP请求的Content-Type。

1) 如果发送的是 Blob 对象,Content-Type 将成为 Blob 的类型。

2) 如果发送一个FormData对象,Content-Type变为multipart/form-data

3) 如果发送 URLSearchParams 对象,则 Content-Type 变为 application/x-www-form-urlencoded

4) 如果发送的是普通字符串,则Content-Type变为text/plain

Javascript实现不同对象的代码can be found here

如果您正在使用 Chrome 并尝试设置 content-type header,由于安全限制,您可能会遇到一些问题:

Uncaught DOMException: Failed to execute 'sendBeacon' on 'Navigator': sendBeacon() with a Blob whose type is not any of the CORS-safelisted values for the Content-Type request header is disabled temporarily. See http://crbug.com/490015 for details.

sendBeacon API not working temporarily due to security issue, any workaround?

因为方法 sendBeacon(..) 不允许 headers 操作,我将它们作为普通字段添加到表单中:

    const formData = new FormData();
    formData.append('authorization', myAuthService.getCachedToken());
    navigator.sendBeacon(myURL, formData);

然后在主机端我添加了一个简单的中间件 class (.Net),它捕获 POST 没有 headers 的请求并从 body 复制它们:

    public class AuthMiddleware
    {
        ...
        ...
        public async Task Invoke(HttpContext context)
        {
            string authHeader = context.Request.Headers["Authorization"];
            if (authHeader == null && context.Request.Method=="POST")
            {
                context.Request.Headers["Authorization"] = string.Format("Bearer {0}",
                    context.Request.Form["authorization"].ToString());
            }

            await _next.Invoke(context);
        }
    }

作为答案发布,因为我不允许 post 在答案下发表评论:

对于 Chrome,navigator.sendBeacon 使用非 CORS 安全列表类型发送 Blob 的问题已在 Chrome 版本 81 中修复,因此现在可以安全使用。 https://bugs.chromium.org/p/chromium/issues/detail?id=724929

对于 IE,卸载事件中的替代方法是使用同步 ajax 请求,因为 IE 不支持 sendBeacon 但在我的例子中支持同步 ajax 调用。

在 Chrome 39 之后您无法使用 JSON 发送数据,由于安全问题已被禁用。

您可以尝试使用纯文本发送数据。但是不要忘记来自后端的解析文本。