Reputation: 73
I am trying to make a Google Home app and I am using webhooks with express.js running on a local server as fulfilment. I would like to use the "request.body.originalDetectIntentRequest.payload.user.userStorage" parameter from the request to identify a user when the webhook sends a post request to my server. The userId given with this request is equal to a log-filename that I store locally for each user where I keep track of different things per user.
I got the idea of handling things like this from an earlier post here on stack overflow. In the answer given by Prisoner, he talks about the importance of the response that you have to send back. So far so good, I tried to implement this in my code by sending a response once the new userId is made but I keep getting the same error :
(node:21236) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:518:11)
So it probably has to do with the fact that I have a line agent.handleRequest(intentMap);
at the end of my code which will handle the request and send a response back to the user itself.
How do I solve this? I already tried to find a solution for this problem but the after trying different things, there is only one thing I can think of which would be to expand the response before sending it with the payload.google.userStorage variable even though I have tried response.write but that was giving me the same error.
My code:
app.post('/', express.json(), (request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
let userStorage = request.body.originalDetectIntentRequest.payload.user.userStorage || JSON.stringify({});
let userId;
console.log("userStorage", userStorage);
userStorage = JSON.parse(userStorage);
console.log("userStorage_after_parsing", userStorage);
if (userStorage.hasOwnProperty('userId')) {
userId = userStorage.userId;
console.log('user was already defined:' + userId);
} else {
let uuid = require('uuid/v4');
checkNewUser = true;
userId = uuid();
userStorage.userId = userId
console.log(userId);
response.send({
'payload': {
'google': {
'userStorage': JSON.stringify(userStorage)
}
}
}
);
//rest of the code
agent.handle(intentMap)
UPDATED QUESTION:
After prisoner's answer, I decided to include the code of the handleWelcome intent where I would like to paste the code into to extend the response with the the userStorage variable:
function handleWelcome(agent) {
//code to add userStorage to the response?
if (checkIfUserVerified() === true) {
agent.add('Hi.');
//updateLog('User just said hi.');
let userObj = readJsonSync(userId + '.json');
userObj.logs.push('User just started talking to this chatbot ' + getCurrentDateTime());
fs.writeFileSync(userId + '.json', JSON.stringify(userObj));
}
else {
close('Please make sure you are a verified user before using this version of SMART4MD. To do so, read the SMART4MD Voice assistant manual.');
agent.setFollowupEvent('GOODBYE');
}
}
Upvotes: 1
Views: 539
Reputation: 50701
Note the comment in the answer you cite where it says
// Make sure you include the userStorage as part of the response
"part of" the response - not the response itself. In your code, you're sending back the JSON to sent the user storage before your Intent Handler is called. Once something is sent, the connection is closed, so nothing further can be sent - including the reply you want. Which is why you get the error.
You don't show any of your Intent Handler functions, so it is difficult to give you an exact answer, but the general solution would be to set the userid in userStorage
as part of a function that each handler calls.
Upvotes: 1