简体   繁体   中英

Express | Cannot set headers after they are sent to the client

I'm trying to make a simple API with passport-jwt and passport-local-mongoose, I set all the JWT functions up and make some routes, like one to register and one to sign in, One ogf these routes receives a get request to list all documents that exists on the database, however: when I try to make this request the server gives me this error:

DError [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:533:11)
    at ServerResponse.header (/home/mdsp/Documents/Challenges/passportJWTMongoose/node_modules/express/lib/response.js:771:10)
    at ServerResponse.json (/home/mdsp/Documents/Challenges/passportJWTMongoose/node_modules/express/lib/response.js:264:10)
    at /home/mdsp/Documents/Challenges/passportJWTMongoose/src/controllers/Document.ts:9:18
    at step (/home/mdsp/Documents/Challenges/passportJWTMongoose/src/controllers/Document.ts:33:23)
    at Object.next (/home/mdsp/Documents/Challenges/passportJWTMongoose/src/controllers/Document.ts:14:53)
    at fulfilled (/home/mdsp/Documents/Challenges/passportJWTMongoose/src/controllers/Document.ts:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:97:5) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

I searched about it and found that this error usually is caused because a double callback or something like that, but I'm not able to specify where is the error and why it's happening this with my code. Here's the Document controller:

import { Request, Response } from "express";

import DocumentModel from "../database/models/Document";

class Document {
  async index(req: Request, res: Response) {
    try {
      const documents = await DocumentModel.find();
      return res.status(200).json(documents);
    } catch (err) {
      console.log(err);
      return res.status(400).json({ error: err.message });
    }
  }
}

export default new Document();

This is my routes file:

import { Router } from "express";

import auth from "./middleware/auth";
import documentController from "./controllers/Document";

const router = Router();

router.get("/", (req, res) => {
  return res.json({ home: "My homepage" });
});

router.get("/documents", auth.requireJWT, documentController.index);

router.post("/auth/register", auth.register, auth.signJWTForUser);

router.post("/auth", auth.signIn, auth.signJWTForUser);

export default router;

And here's is the auth.ts, where I configured all the passport and JWT functions to refister, sign in and request a JWT token:

import { config } from "dotenv";
config();
import passport from "passport";
import JWT from "jsonwebtoken";
import { Strategy, ExtractJwt } from "passport-jwt";
import { Request, Response, NextFunction } from "express";

import UserModel from "../database/models/User";

interface IRequestUser extends Express.User {
  _id?: string;
  email?: string;
  firstName?: string;
  lastName?: string;
}

const jwtSecret = process.env.JWT_SECRET!;
const jwtAlgorithm = "HS256";
const jwtExpiresIn = "7 days";

passport.use(UserModel.createStrategy());

async function register(req: Request, res: Response, next: NextFunction) {
  try {
    const { email, firstName, lastName, password } = req.body;

    const user = new UserModel({
      email: email,
      firstName: firstName,
      lastName: lastName,
    });

    const registeredUser = await UserModel.register(user, password);

    req.user = registeredUser;

    return next();
  } catch (err) {
    console.log(err.message);
    return next(err);
  }
}

passport.use(
  new Strategy(
    {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: jwtSecret,
      algorithms: [jwtAlgorithm],
    },
    async (payload, done) => {
      try {
        const foundUser = await UserModel.findById(payload.sub);

        foundUser && done(null, foundUser);

        done(null, false);
      } catch (err) {
        console.log(err.message);
        done(err, false);
      }
    }
  )
);

function signJWTForUser(req: Request, res: Response) {
  if (req.user) {
    const user: IRequestUser = req.user;

    const token = JWT.sign({ email: user.email }, jwtSecret, {
      algorithm: jwtAlgorithm,
      expiresIn: jwtExpiresIn,
      subject: user._id!.toString(),
    });

    return res.json({ token });
  }
}

export default {
  initialize: passport.initialize(),
  register,
  signIn: passport.authenticate("local", { session: false }),
  requireJWT: passport.authenticate("jwt", { session: false }),
  signJWTForUser,
};

What should I improve on my controller and what is causing this error?

Your passport strategy setup custom callback is sending two reposes when you find the user.

const foundUser = await UserModel.findById(payload.sub);

foundUser && done(null, foundUser);
done(null, false);    //<<<- HERE

I'm not a Passport.js expert - but you should call this just once as per the documentation.

const foundUser = await UserModel.findById(payload.sub);
done(null, foundUser);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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