req is undefined when using req.accept inside nested function

Tags: , ,



I’ve recently come across a problem when working with the built-in req.accepts, req.acceptsLanguages, req.acceptsCharsets, and req.acceptsEncodings functions in express.

I have an express middleware function like this:

function acceptCheckpoint(acceptOpts) {
  // Calling the following function results in a TypeError.
  function checkAccept(req, res, opts) {
    let acceptFunction = null;

    switch (opts.whichAccept) {
    case "type":
      acceptFunction = req.accepts;
      break;
    case "lang":
      acceptFunction = req.acceptsLanguages;
      break;
    case "charset":
      acceptFunction = req.acceptsCharsets;
      break;
    case "encoding":
      acceptFunction = req.acceptsEncodings;
      break;
    default:
      acceptFunction = req.accepts;
      break;
    }

    return acceptFunction(opts.acceptedTypes);
  }

  return (req, res, next) => {
    const accepted = [];

    Object.getOwnPropertyNames(acceptOpts).forEach(key => {
      if (key === "ignoreAcceptMismatch") { return; }
      const acceptsType = checkAccept(req, res, {
        whichAccept: key,
        acceptedTypes: acceptOpts[key]
      });
      accepted.push(acceptsType);
    });
    if (accepted.some(type => !type) && !acceptOpts.ignoreAcceptMismatch) {
      res.type("html");
      res.status(406);
      res.send("<h1>406 Not Acceptable.</h1>");
      return;
    }
    next();
  };
}

Which, in theory, should work. But the program keeps complaining and logs this error:

TypeError: Cannot read property 'headers' of undefined
    at new Accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/accepts/index.js:37:22)
    at Accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/accepts/index.js:34:12)
    at req.accepts (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/request.js:133:16)
    at checkAccept (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:208:12)
    at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:216:27
    at Array.forEach (<anonymous>)
    at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/Lib/middleware.js:214:44
    at Layer.handle [as handle_request] (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/index.js:317:13)
    at /Users/hortoncheng/Desktop/Programs/colonialwars/dev/node_modules/express/lib/router/index.js:284:7

The thing is, when I use req.accepts or one of those .accepts functions in the main function (acceptCheckpoint), like this:

// Pretend we're in acceptCheckpoint...
// This works.
accepted.push(req.accepts("html"));

It works. And, when I log the req object in either of those functions, it returns the expected value. I’ve also tried logging the req object in the request.js file of the express module, and there, it returned undefined. So that led me to believe that it was a problem with express itself. I tried deleting package-lock.json and node_modules, and then running npm install. Didn’t fix it. And yes, I’m calling the express middleware function correctly. Any idea why this is happening?

I’m using express v4.17.1, Node.JS v12.18.1, and NPM v6.14.5.

Answer

The function is presumably trying to get req from its this context. But you’re not passing functions with context.

Change this line:

return acceptFunction(opts.acceptedTypes);

to:

return acceptFunction.call(req, opts.acceptedTypes);

The first argument to the call() method is the object that should be used as this in the called function.



Source: stackoverflow