为什么我没有看到 nodejs `response.addTrailers` 添加的 headers

Why am I not seeing the headers added by nodejs `response.addTrailers`

编辑

我已经能够使用 curl 直接联系后端,它肯定会发送尾随 header。它似乎在 body 之后的输出流中,所以我的 api 例程可能需要稍后检查它,或者它可能无法通过 nginx。

第二次编辑 我使用 curl 直接访问后端我看到尾随 header。联系前端(Nginx)我没看到

原始问题

我正在尝试创建一个节点服务器来响应来自客户端的 API 请求。

此服务器是由 nginx 代理的后端,它可能正在剥离 headers 但是... 这是它的反向代理配置,所以我不这么认为

  location /api/ {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_pass http://localhost:2040;
    proxy_redirect off;
    proxy_buffering off;
    proxy_cache off;
  }

服务器正在处理这样的特定 api 请求

router.post('/api/process', async (req,res) => {
    res.writeHead(200, {
      'Content-Type': 'application/json',
      'Cache-Control': 'no-cache',
      'X-Accel-Buffering': 'no',
      'Trailer': 'API-Status'
    });
    try {
        response = await callApiProcessor(req.params);
        res.write(JSON.stringify(response));
        res.addTrailers({'API-Status': 'OK'})
    catch (e) {
        res.addTrailers({'API-Status': e.toString()});
    }
    res.end();
});

在浏览器端,我正在使用这样的获取api

export default function api(url, params, signal) {
  const options = {
    credentials: 'same-origin',
    method: 'post',
    headers: new Headers({
      'content-type': 'application/json'
    }),
    body: JSON.stringify(params)
  };
  if (signal) options.signal = signal;
  return window.fetch('/api/' + url, options).then(response => {
    if (!response.ok || response.headers['API-Status'] !== 'OK') {
      if (response.ok) console.warn('SERVER API Error:', response.headers['API-Status']);
      window.dispatchEvent(new LogoffRequest());
      //put us back to home
      window.history.pushState({}, null, '/');
      window.dispatchEvent(new LocationAltered());
      return {};
    } else {
      return response.json();
    }
  });
}

通过在 api 模块中放置断点,我在响应中看不到任何 header。因此,尽管 response.ok 是正确的,但我仍然在我的应用程序中强制客户端注销。

查看 Chrome 的 Dev Tools Networking 选项卡,我看不到 API-Status,尽管我确实看到了预告片 header。

正如我上面提到的,nginx 是拖车的片段。我在 nginx 邮件列表上问过这个问题,答案是它没有实现传递尾随 headers。但是,我已经解决了这个问题,即您如何在服务器中,在发送 header 后注意到一个问题(因为您已经开始发送 body)并传回本质上的内容403 或 500 服务器响应。

在服务器中,我有两个类似的例程 - 这是 500 错误之一,但 403 是相同的,除了状态代码和我记录客户端的 ip 地址 (req.headers['x-forwarded-for'])。

  function errored(req,res,message) {
    debug('In "Errored"');
    logger('error', `${message} with request url of ${req.originalUrl}`);
    res.statusCode = 500;
    res.end('---500---'); //definitely not json, so should cause api to throw even if attempt to send status code is to no avail.

  }

如果在 body 导致发送 200 之前调用此方法,则会发送实际的 500,否则本质上应该是 json 的内容会被文本打断。

然后在客户端我有我的 api 调用函数:

import { ApiError } from "./events.js";

export default async function api(url, params, signal) {
  const options = {
    credentials: 'same-origin',
    method: 'post',
    headers: new Headers({
      'content-type': 'application/json'
    }),
    body: JSON.stringify(params)
  };
  if (signal) options.signal = signal;
  let text;
  try {
    const response = await window.fetch('/api/' + url, options);
    if (!response.ok) throw new ApiError(response.status); 
    text = await response.text();
    return JSON.parse(text);
  } catch (err) {
    if (err.type === 'api-error') throw err; //just 
      //we failed to parse the json - the actual code should be in the text near the end;
    throw new ApiError(parseInt(text.substr(-6,3),10));    

  }


}

这会尝试请求,首先将其解析为文本(您只能读取 body 一次,所以我们将其作为文本进行,因此我们有一个副本,然后尝试将其解析为 JSON).如果最终解析抛出,或者响应拾取响应错误,我们可以确定是哪个抛出我的 ApiError 函数。在其他地方,我已将事件侦听器附加到未捕获的承诺拒绝

  _promiseRejection(e) {
    if (this.anError) return;
    e.preventDefault();
    const possibleError = e.reason;
    let message
    if (possibleError.type === 'api-error') {
      this._serverError(possibleError)
    } else {
      const message = `Client Error: Uncaught Promise Rejection with reason ${e.reason} has occured`;
      api('/session/log', { type: 'Error', message: message });
      this.dispatchEvent(new SessionStatus({ type: 'error' }));
      this.anError = true;
    }
  }

最后一个函数位于我的应用程序顶部的自定义元素中。大多数时候它的内容是隐藏的,但是当变量 this.anError 为真时它显示内容。我还有一些特殊事件可以在出现 403(但不是 500)的情况下注销用户,并在出现任何错误时切换到主页。