Angular/Express GET 调用有时有效,有时失败。出现错误 [ERR_HTTP_HEADERS_SENT]:将它们发送到客户端后无法设置 headers

Angular/Express GET Call sometimes works, sometimes fails. Getting Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

我将为 get 调用列出流程的每个步骤。我网站上的每个 GET 调用都设置完全相同,并且它们在 99% 的时间内都有效。一些 API 调用有时会导致 404,我不知道为什么。

该流程适用于此时尚中的 100 次 GET 调用。

流量为:

  1. Angular 页面调用数据库服务。
  2. DB 服务向后端路由器发送 GET 请求。
  3. 路由器然后转到中间件来验证令牌。
  4. 路由器然后点击控制器等待从数据库中获取数据。
  5. 控制器实际调用 db_api SQL
  6. 数据为 return 到 Angular。

当前错误:

ode:18) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
ERROR FOUND o_l_vfn: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client


2020-10-23 13:02:41(node:18) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41at ServerResponse.setHeader (_http_outgoing.js:535:11)7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41at ServerResponse.header (/usr/src/app/node_modules/express/lib/response.js:771:10)7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41at ServerResponse.contentType (/usr/src/app/node_modules/express/lib/response.js:599:15)7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41at ServerResponse.sendStatus (/usr/src/app/node_modules/express/lib/response.js:357:8)7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41at get (/usr/src/app/controllers/o_l_ar.js:15:5)7c0b5a9e69b741e1a231a5cc02d8583e
2020-10-23 13:02:41(node:18) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated eit

(node:18) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function
 without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection,
 use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 4)
 
 
dashboard.component.ts:171 ERROR: {"headers":{"normalizedNames":{},"lazyUpdate":null},"status":404,"statusText":"OK","url":/api/o_l_ar?issuerid=1","ok":false,"name":"HttpErrorResponse",
"message":"Http failure response for/api/o_l_ar?issuerid=1: 404 OK","error":"<!DOCTYPE html>\n<html lang=\"en\">\n
<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /api/o_l_ar</pre>\n</body>\n</html>\n"}

dashboard.component.ts -- 包含对我的 Angular 服务的调用

import { Component, OnInit } from '@angular/core';
import { DbService, OperationVFN} from '../db.service';
import { ActivatedRoute } from '@angular/router';
import {MatDialog} from '@angular/material/dialog';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['../global.css']
})
export class DashboardComponent implements OnInit {

  operationvfns: Array<OperationVFN>;
  constructor(
    private DbService: DbService,
    private route: ActivatedRoute,
    public dialog: MatDialog
  ) {}


rungetOperationVFN(id) {
  return new Promise((resolve, reject) => {
  this.DbService.getOperationVFN(id).subscribe(operationvfns => this.operationvfns = operationvfns,
    (err) => { 
      console.log('ERROR: ' + JSON.stringify(err)); 
      reject('Rejected');
  },() => {
    resolve(1);
  });
  })
  }
  
  async ngOnInit() {
    const id = this.route.snapshot.paramMap.get('id');
    try { await this.rungetOperationVFN(id); } catch (e) {console.log('ang comp: ' + e)}
    }
  }

db.service.ts -- 调用我的后端 Node/Express 服务器

import { Injectable } from '@angular/core';
import { HttpClient , HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Router, ActivatedRoute } from '@angular/router';
export class OperationVFN {
  OPERATION_TYPE: string;
  OPERATION_ID: string;
  SIMPLE_NAME: string;
  LINK_NAME: string;
  TABLENAME: string;
  MAX_CALC_DT: string;
}

 const hosturl = 'https://webiste.com/api/'; 

@Injectable()

export class DbService {
  constructor(private _http: HttpClient,
    private route: ActivatedRoute,
    private router: Router) {}
getOperationVFN(id): Observable<OperationVFN[]> {
  const url = hosturl +'o_l_vfn';
      const httpOptions = {
      headers: new HttpHeaders({
        'Access-Control-Allow-Origin':  '*',
        'Content-Type': 'application/json'
      }),
      withCredentials: true,
      params: {
        'issuerid': id
      }
    };
  return this._http.get(url, httpOptions)
  .pipe(
    map((res) => {
      console.log(res);
      return <OperationVFN[]> res;
    })
  );
}
}

router.js -- API 调用的路由器

const express = require('express');
const router = new express.Router();
var authMiddleware = require('../middleware/AuthMiddleware.js');

const o_l_vfn = require('../controllers/o_l_vfn.js');
router.get('/o_l_vfn', authMiddleware.Validate, o_l_vfn.get);

module.exports = router;

用于验证 API 调用 header

令牌的中间件

var jwkToPem = require('jwk-to-pem'),
jwt = require('jsonwebtoken');
const request = require('request');


exports.Validate = function(req, res, next) {


  if (req.headers['authorization']) {
      const token = req.headers['authorization'];
    request({
        url : `oauth/.well-known/jwks.json`,
        json : true
     }, function(error, response, body){
        if (!error && response.statusCode === 200) {
            pems = {};
            var keys = body['keys'];
            for(var i = 0; i < keys.length; i++) {
                 var key_id = keys[i].kid;
                 var modulus = keys[i].n;
                 var exponent = keys[i].e;
                 var key_type = keys[i].kty;
                 var jwk = { kty: key_type, n: modulus, e: exponent};
                 var pem = jwkToPem(jwk);
                 pems[key_id] = pem;
            }
            var decodedJwt = jwt.decode(token, {complete: true});
                 if (!decodedJwt) {
                     console.log("Not a valid JWT token");
                     res.status(401);
                     return res.send("Not a valid JWT token");
                }
             var kid = decodedJwt.header.kid;
                 var pem = pems[kid];
                 if (!pem) {
                     console.log('Invalid token - decodedJwt.header.kid');
                     res.status(401);
                     return res.send("Invalid token - decodedJwt.header.kid");              
                 }
             jwt.verify(token, pem, function(err, payload) {
                     if(err) {
                         console.log("Invalid Token - verify");
                         res.status(401);
                         return res.send("Invalid token  - verify");
                     } else {
                          console.log("Valid Token.");
                          return next();
                     }
                });
        } else {
              console.log("Error! Unable to download JWKs");
              res.status(500);
              return res.send("Error! Unable to download JWKs");
        }
    });
      } else {
      }
      return next();
  }

controller/o_l_vfn.js -- 控制器

const o_l_vfn = require('../db_apis/o_l_vfn.js');
const env = require('../config/dbconfig.js');

async function get(req, res, next) {
  try {
    const context = {};

    context.tredb = env.tredb;
    context.id = req.query.id;

    const rows = await o_l_vfn.find(context);
    res.send(rows);
    } catch (err) { 
      console.log('ERROR FOUND o_l_vfn: ' + err)
res.sendStatus(404);
    next(err);
  }
}
module.exports.get = get;

db_api -- o_l_vfn.ts

const database = require('../services/database.js');

async function find(context) {
  let query = "SELECT * from " + context.tredb + ".OPERATIONS WHERE Operation_Type = 'DR' and id = '" + context.id + "' ORDER BY Operation_Id ASC";
  const binds = {};


console.log(query);
  const result = await database.simpleExecute(query, binds);
  console.log(result.rows);
  return result.rows;
}

module.exports.find = find;

您需要将 Validate 函数底部的 return next(); 移动到 else:

else {
  return next();
}

按照您目前的方式,当提供 authorization header 时,它会调用 next 两次。