简体   繁体   中英

Node/Express route not waiting for Redis server callback

What specific changes need to be made to the code below so that the res.json(...) command is NOT sent until any open calls to the Redis client.somecommand(..) have returned?

The code below is throwing an error related to the client.hmset(uname, { ... } trying to set a response header after res.json(...) was called. When I move the return res.json() commands to right after the end of the client.exists(uname, function(err, reply) { ... } conditional block, instead of their current locations inside the block, the anonymous token value is sent to the client app instead of the generated token value. This indicates that the callbacks to the Redis server have not returned.

How do I change the code below so that the res.json( ... ) commands cannot run until the Redis server callback has been returned? Ideally, there would be some conditional to wait a certain period before sending an error message if the Redis server callback takes too long.

Redis is added to the routes.js file containing all the code below, by adding the following two lines to the top of the file:

var redis = require('redis');
var client = redis.createClient();

Here are the various calls to the Redis server in the code below:

client.exists(uname, function(err, reply) { ... }
client.hgetall(uname, function(err, object) { ... }
client.hmset(uname, { ... }
client.expire(uname, 10);

The complete code for the Node.js/Express.js API route is:

app.get('/user**', function(req, res) {
    console.log("You Hit The User Route TOP");
    request({
        method: 'GET',
        url: authServer + '/uaa/user', 
        json: true, 
        auth: {
            user: null,
            password: null,
            sendImmediately: true,
            bearer: bearerToken 
        }
    }, function (error, response, body) {
        if(error){
          console.log('ERROR with user request.');
          return res.sendStatus(500);  
        }
        else {
            var uname = '';var jwtUser = 'empty';var jwtJSON = { "token" : "anonymous" }
            console.log(response.statusCode);
            if(body['name']){ 
                uname = body['name'];console.log('uname is: ');console.log(uname);
                if(uname.length > 0) {
                    scopesLocal = body['oauth2Request'].scope.toString();
                    client.exists(uname, function(err, reply) {//Check to see if a Redis key for the user already exists
                        if (reply === 1) {//a redis key DOES exist
                            console.log('\"'+uname+'\" exists');
                            client.hgetall(uname, function(err, object) {//retrieve all the values in the hash/object that we just set
                            if(object) {
                                if(object["jwt"]) { 
                                    console.log('object[\"jwt\"] is: ');console.log(object["jwt"]); 
                                    jwtJSON = { "token" : object["jwt"] };
                                    console.log('jwtJSON is: ');console.log(jwtJSON);
                                    return res.json(jwtJSON);
                                }
                            }
                        });
                        } else {//a redis key DOES NOT exist
                            console.log('\"'+uname+'\" doesn\'t exist');
                            jwtUser = generateJwt(uname, authoritiesLocal);
                            client.hmset(uname, {//store a hash/object
                                'AccessToken': body['details'].tokenValue,
                                'TokenType': body['details'].tokenType,
                                'Authenticated': body['authenticated'],
                                'Principal': body['principal'],
                                'Scopes': scopesLocal.toString(), 
                                'Authorities' : authoritiesLocal,
                                'jwt' : jwtUser
                            });
                            jwtJSON = { "token" : jwtUser };console.log('jwtJSON is: ');console.log(jwtJSON);
                            return res.json(jwtJSON);
                        }
                        client.expire(uname, 10);//set the key to expire in 10 seconds.  use this to manage session length
                    });//end of Redis conditional block
                    console.log('jwtJSON is: ');console.log(jwtJSON);
                } else { console.log('uname is empty!'); }
                return res.json(jwtJSON);
            }
        };
    });
    console.log("You Hit The User Route BOTTOM");
});

The error message in the nodemon terminal is:

_http_outgoing.js:346
    throw new Error('Can\'t set headers after they are sent.');
    ^

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
    at ServerResponse.header (/home/user/nodejs_apps/oauth_seed_app/node_modules/express/lib/response.js:719:10)
    at ServerResponse.json (/home/user/nodejs_apps/oauth_seed_app/node_modules/express/lib/response.js:247:10)
    at Command.callback (/home/user/nodejs_apps/oauth_seed_app/app/routes.js:112:44)
    at normal_reply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:714:21)
    at RedisClient.return_reply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:816:9)
    at JavascriptRedisParser.Parser.returnReply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:188:18)
    at JavascriptRedisParser.execute (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis-parser/lib/parser.js:413:12)
    at Socket.<anonymous> (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:267:27)
    at emitOne (events.js:90:13)
    at Socket.emit (events.js:182:7)
    at readableAddChunk (_stream_readable.js:153:18)
    at Socket.Readable.push (_stream_readable.js:111:10)
    at TCP.onread (net.js:534:20)

I read this posting about the specific error message. And I read this other posting about how to wait for a callback. I also read this posting about Redis callbacks to Node. But I do not see how to apply the answers to either of those other postings to the Redis callback problem in the code above.

The problem was that the return res.json(jwtJSON); commands in the OP were NOT segregated into discrete if...else blocks.

The solution is:

if(something) {
    //populate jwtJSON with a real JWT
    return res.json(jwtJSON);
} else {
    return res.json(jwtJSON);//this leaves the anonymous value
}  

Doing this segregation for every nested condition in the above code got rid of the problem.

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