【问题标题】:Why am I not seeing the headers added by nodejs `response.addTrailers`为什么我没有看到 nodejs `response.addTrailers` 添加的标头
【发布时间】:2020-06-02 10:34:09
【问题描述】:

编辑

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

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

原始问题

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

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

  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();
});

在浏览器端,我正在使用这样的 fetch 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 模块中放置断点,我在响应中看不到任何标题。所以尽管response.ok 是真的,我仍然在我的应用程序中强制客户端注销。

查看 Chrome 的 Dev Tools Networking 选项卡,我看不到 API 状态,尽管我看到了 Trailer 标头。

【问题讨论】:

    标签: node.js http-headers nginx-reverse-proxy


    【解决方案1】:

    正如我上面提到的,nginx 是预告片的片段。我在 nginx 邮件列表上问过这个问题,答案是它没有实现传递尾随标头。但是,我已经解决了这个问题,即您如何在服务器中,在发送标头后发现问题(因为您已经开始发送正文)并返回本质上是 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.
    
      }
    

    如果在正文导致发送 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));    
    
      }
    
    
    }
    

    这会尝试请求,首先将其解析为文本(您只能读取一次正文,因此我们将其作为文本执行,因此我们有一个副本,然后尝试将其解析为 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 为 true 时,它​​会显示内容。我还有一些特殊事件可以在 403(但不是 500)的情况下注销用户,并在出现任何错误时切换到主页。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-15
      • 2021-01-05
      • 1970-01-01
      • 2015-03-31
      • 1970-01-01
      • 2021-02-22
      • 2015-06-14
      • 2017-08-30
      相关资源
      最近更新 更多