I've been working on troubleshooting a very simple application and cannot assess why the request body of POST is undefined. I have used express.json() and body-parser and changed the Content-Type of the request to different formats and have gotten nothing back.
This is the backend code relevant to the issue
const morgan = require("morgan");
const { response } = require("express");
const { format } = require("morgan");
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.static("build"));
app.use(cors());
app.use(express.json());
app.use(morgan("combined"));
app.post("/api/phonebook/", (req, res) => {
const body = req.body;
console.log(req.body);
console.log(body.content);
if (!body.name) {
return res.status(404).json({
error: "no name",
});
}
const personObj = {
name: body.name,
phone: body.phone,
id: generateId(),
};
phonebook = phonebook.concat(personObj);
response.json(personObj);
});
And this is an example POST
POST http://localhost:3000/api/phonebook/
Content-Type: application/json
{
"name": "Bob",
"phone": 123-456
}
The server is up and running and doing GET and DELETE requests both work.
POST http://localhost:3000/api/phonebook/
Content-Type: application/json
{
"name": "Bob",
"phone": 123-456
}
On request parameter body, 123-456 Isn't number so it should have " " to be "123-456". Because 123-456 type is null so the parameter is undefined. Parsing stiringified number into integer is needed on the server.
Updated
Response parameters in callback func should be used properly. response.json on end of callback function should be edited to res.json as the callback function defined (res, req).
Related
My frontend code:
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', {user:name})
.then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
The user variable holds the username and the function sends that username to the backend to get the data. Don't worry, the user variable does hold the username, i have checked it thoroughly.
My backend code:
const mysql = require('mysql2')
const express = require('express')
const cors = require('cors');
const app = express()
app.use(cors());
app.use(express.json())
app.get("/watchlater", (request, response)=>{
const user = request.body.user;
//console.log(user);
});
So basically, it will get the username and run the query. The problem is it does not get the username at all from the frontend. I tried console logging the variable user but to no avail. It returns empty.
The second argument in the axios.get() function is expecting a config object. Checkout the axios documentations on instance method and request config.
In short, in the frontend part, pass your payload into the data field of the config object, as shown in the example below.
const config = {
headers: { Authorization: token },
data: { user:name }
}
const response = await axios.get(`${url}`, config)
You need to send parameter using params object of config in case of get request. Your frontend request should change to this,
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', { params: { user: name }
}).then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
In your express endpoint you should receive it as,
app.get("/watchlater", (request, response)=>{
const user = request.params.user;
//console.log(user);
});
This is my server.js file code . I am trying to push the JSON content in the user object , but i am getting following error. Please tell me where i am going wrong.
const express = require('express')
const app = express()
const bcrypt = require('bcrypt')
const bodyParser = require('body-parser')
app.use(express.json())
const users = []
app.get('/users', (req, res) => {
JSON.stringify(users)
res.json(users)
})
app.post('/users', (req, res) => {
const user = {
name: req.body.name,
password: req.body.password
}
users.push(user)
res.status(201).send()
})
app.listen(3000, console.log("server started"));
I used an extension in VS code called REST client.
GET http: //localhost:3000/users
#####
POST http: //localhost:3000/users
Content-Type: application/json
{
"name": "Tanay",
"password": "password"
}
When I'm firing POST request it shows the error - SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at parse (C:\Users\TANAY RAJ\Desktop\nodePassport\Wsimplified\node_modules\body-parser\lib\types\json.js:89:19)
at C:\Users\TANAY RAJ\Desktop\nodePassport\Wsimplified\node_modules\body-parser\lib\read.js:121:18
at invokeCallback (C:\Users\TANAY RAJ\Desktop\nodePassport\Wsimplified\node_modules\raw-body\index.js:224:16)
at done (C:\Users\TANAY RAJ\Desktop\nodePassport\Wsimplified\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (C:\Users\TANAY RAJ\Desktop\nodePassport\Wsimplified\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (events.js:322:22)
at endReadableNT (_stream_readable.js:1187:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
Can be something wrong with the user variable. Can you check this:
const user={'name':req.body.name,'password':req.body.password}
Update
I tried out:
var data = [];
const user={'name':"Deshan",'password':"password"}
data.push(user);
console.log(data);
And the result was as follow:
[ { name: 'Deshan', password: 'password' } ]
So it maybe a problem with the request data.
I'm created a chat-bot using 'botact' library, but
when I'm trying to verificate my bot on vk-community API working page I receive an error in 'Windows PowerShell' (Here I started the server for bot):
TypeError: Cannot read property 'fwd_messages' of undefined
at Botact.getLastMessage (C:\Users\whoami\Desktop\Bot-test\node_modules\botact\lib\utils\getLastMessage.js:2:11)
at Botact.module.exports (C:\Users\whoami\Desktop\Bot-test\node_modules\botact\lib\methods\listen.js:29:28).
The file 'getLastMessage.js' contains this code:
const getLastMessage = (msg) => {
if (msg.fwd_messages && msg.fwd_messages.length) {
return getLastMessage(msg.fwd_messages[0])
}
return msg
}
module.exports = getLastMessage
So I don't know much about botact but according to the code when you are hitting the / route, you need to pass a body containing an object property.
Now as this is bot framework for vk bots maybe it automatically sends the request body. You can make sure by logging the request body.
server.post('/', async (req,res)=>{
console.dir(req.body);
await bot.listen(req, res);
});
/lib/methods/listen.js:
const { type, secret, object, group_id } = framework === 'koa'
? args[0].request.body
: args[0].body
...
...
...
const { events, middlewares } = actions
const forwarded = this.getLastMessage(object)
Now, when you do bot.listen express passes req as first argument. and { type, secret, object, group_id } these fields get distructured from the req.body.
And then object is getting passed to the getLastMessage function.
So for the request body in minimum you would need
{ "object": {} }
Here is the 200 OK output that I got after added that to the request body from Postman
POC Code:
const express = require("express");
const bodyParser = require("body-parser");
const { Botact } = require("botact");
const server = express();
const bot = new Botact({
token: "token_for_my_group_i_just_hided_it",
confirmation: "code_for_my_group_i_just_hided_it"
});
server.use(bodyParser.json());
server.post("/",bot.listen);
server.listen(8080);
I am attempting to get the silly name maker to work using a webhook hosted on my server. Using PTs-V2 I am able to see the JSON post from Dialogflow.
...{"color":"red","number":6}...
Using Hurl.it I am able to successfully send the post to my server and get the correct response.
{
"speech": "Alright, your silly name is red 6! I hope you like it. See you next time."...
But when I try to use my own server for Dialogflow it does not work. Not sure if there is something I'm missing.
Webhook on server
'use strict';
const https = require('https');
const fs = require('fs');
var privatekey = fs.readFileSync('...');
var certificate = fs.readFileSync('...');
var credentials = {key: privatekey, cert: certificate};
process.env.DEBUG = 'actions-on-google:*';
var DialogflowApp = require('actions-on-google').DialogflowApp;
const express = require('express');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.json({type: 'application/json'}));
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(443);
const NAME_ACTION = 'make_name';
const COLOR_ARGUMENT = 'color';
const NUMBER_ARGUMENT = 'number';
app.get('/', function(req, resp) {
resp.send("Hello!");
console.log('app.get');
});
app.post('/', function (request, response){
console.log('app.post');
const app = new DialogflowApp({request: request, response: response});
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body: ' + JSON.stringify(request.body));
// Make a silly name
function makeName (app) {
let number = app.getArgument(NUMBER_ARGUMENT);
let color = app.getArgument(COLOR_ARGUMENT);
app.tell('Alright, your silly name is ' +
color + ' ' + number +
'! I hope you like it. See you next time.');
}
let actionMap = new Map();
actionMap.set(NAME_ACTION, makeName);
app.handleRequest(actionMap);
});
Thanks for any help.
Edit:
Response from Dialogflow when I am running Apache server for website on port
"status": {
"code": 206,
"errorType": "partial_content",
"errorDetails": "Webhook call failed. Error: Failed to parse webhook JSON response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $.",
"webhookTimedOut": false
},
Response when the JS server is running on the port and not Apache
"status": {
"code": 206,
"errorType": "partial_content",
"errorDetails": "Webhook call failed. Error: Webhook response was empty.",
"webhookTimedOut": false
},
Dialogflow Setup (screenshots)
Fulfillment
Intent that calls webhook
Result when Apache webserver is running
Result when JS webhook is running
My JS server never receives a post.
I am trying to custom handle response sent to caller due to RAML specification failure. At the moment my code does the following.
const cfg = require("./cfg");
const log = require('./logging');
const RAML = require('osprey');
const startMessage = "My Service started on port " + cfg.SERVER_PORT + " at " + cfg.API_MOUNT_POINT;
// start an express server
const start = x => {
// server dependencies
const fs = require('fs'),
express = require('express'),
app = express(),
router = express.Router(),
bodyParser = require('body-parser'),
api = require('./api');
RAML.loadFile(cfg.API_SPEC).then(_raml => {
app.use(bodyParser.json({ extended: true }));
// hide the useless "powered by express" header
app.disable('x-powered-by');
// RAML validation
app.use(cfg.API_MOUNT_POINT, _raml);
app.use(cfg.API_MOUNT_POINT, api);
})
.then(v => {
app.listen(cfg.SERVER_PORT, function() {
log.info(startMessage);
});
})
.catch(e => log.error(e));
}
This works well but the response sent to caller when validation fails is shown below.
{
"errors": [
{
"type": "json",
"dataPath": "redeemtype",
"keyword": "required",
"schema": true,
"message": "Missing required property: redeemtype"
}
],
"stack": "BadRequestError: Request failed to validate against RAML definition\n at createValidationError (/Volumes/Devel/dollardine/node_modules/osprey-method-handler/osprey-method-handler.js:735:14)\n at ospreyJsonBody (/Volumes/Devel/dollardine/node_modules/osprey-method-handler/osprey-method-handler.js:448:21)\n at handle (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:56:16)\n at dispatch (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:39:20)\n at next (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:37:24)\n at jsonParser (/Volumes/Devel/dollardine/node_modules/body-parser/lib/types/json.js:94:7)\n at handle (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:56:16)\n at dispatch (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:39:20)\n at middleware (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:41:16)\n at /Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:10:16\n at ospreyContentType (/Volumes/Devel/dollardine/node_modules/osprey-method-handler/osprey-method-handler.js:325:17)\n at handle (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:56:16)\n at dispatch (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:39:20)\n at next (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:37:24)\n at ospreyMethodHeader (/Volumes/Devel/dollardine/node_modules/osprey-method-handler/osprey-method-handler.js:262:12)\n at handle (/Volumes/Devel/dollardine/node_modules/compose-middleware/lib/index.js:56:16)"
}
This is great, but I do not want to send all this info to caller. I want just log it locally and just sent {"code": 400, "message": "Invalid input"}
How can I make osprey to give me ability to handle the error response?
I found answer to my own question. In case anyone in future gets stuck here.
const start = x => {
// server dependencies
const fs = require('fs'),
express = require('express'),
app = express(),
router = express.Router(),
bodyParser = require('body-parser'),
api = require('./api');
const ramlConfig = {
"server": {
"notFoundHandler": false
},
"disableErrorInterception": true
}
osprey.loadFile(cfg.API_SPEC, ramlConfig).then(_raml => {
app.use(bodyParser.json({ extended: true }));
// hide the useless "powered by express" header
app.disable('x-powered-by');
// RAML validation
app.use(cfg.API_MOUNT_POINT, _raml);
app.use(customNotFoundHandler);
app.use(ramlErrorChecker);
app.use(cfg.API_MOUNT_POINT, api);
//app.use(ramlErrorChecker);
})
.then(v => {
app.listen(cfg.SERVER_PORT, function() {
log.info(startMessage);
});
})
.catch(e => log.error(e));
}
const ramlErrorChecker = (err, req, res, next) => {
if (err) {
log.error("RAML validation failed. Reason: " + err);
res.status(400);
return res.json(buildResponseJSON(400));
}
return next();
}
const customNotFoundHandler = (req, res, next) => {
// Check for existence of the method handler.
if (req.resourcePath) {
return next()
}
res.status(404);
return res.json({"message": "The path is not found"});
}
The important part is ramlConfig which helps in putting some customisation. By setting "disableErrorInterception" to true, we are taking over the error handling which is much better for standardisation and most importantly hiding the fact the RAML is being used. Setting "notFoundHandler" to false means, the undeclared routes are reject gracefully instead of random html.