Getting a dreaded JSON error .
I am using an external API that allegedly takes a POST to add a user to a group.
in my nodeJS express app - I want to pass-thru the data coming from my app to the external API.
my "GET" methods work - but now I am trying to take a form submitted to my app, and pass the data to an external API using "POST".
Here is the code I am testing (assume the api url and credentials are correct - and NOT the issue)
I have tested the external API passing the same JSON object directly to the external API using Postman, and it works without error.
const express = require('express');
const router = express.Router();
const https = require('https');
function callExternalAPI( RequestOptions ) {
return new Promise((resolve, reject) => {
https.request(
RequestOptions,
function(response) {
const { statusCode } = response;
if (statusCode >= 300) {
reject(
new Error( response.statusMessage )
);
}
const chunks = [];
response.on('data', (chunk) => {
chunks.push(chunk);
});
response.on('end', () => {
const result = Buffer.concat(chunks).toString();
resolve( JSON.parse(result) );
});
}
)
.end();
})
}
router.get('/thing', /*auth,*/ ( req, res, next ) => {
callExternalAPI(
{
host: 'api_url',
path: '/list',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
}
}
)
.then(
response => {
console.log(response);
}
)
.catch(
error => {
console.log(error);
}
);
});
router.post('/thing', /*auth,*/ ( req, res, next ) => {
callExternalAPI(
{
host: 'api_url',
path: '/addThing',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
},
data: {
'group_id': req.body.group_id,
'person_id': req.body.person_id
}
}
)
.then(
response => {
console.log(response);
}
)
.catch(
error => {
console.log(error);
}
);
});
module.exports = router;
console logging the req.body looks lie this
{ group_id: '45b62b61-62fa-4684-a058-db3ef284f699', person_id: '3b1c915c-3906-42cf-8084-f9a25179d6b2' }
And the error looks like this
undefined:1
<html><title>JSpring Application Exception</title>
<h2>JSpring Exception Stack Trace</h2>
<pre>SafeException: FiberServer.parsers.parseJSONBuf(): JSON parse failed.
^
SyntaxError: Unexpected token < in JSON at position 0
Granted the console.log of the req.body does not have the required double quotes, but I think that is just the log dump format - but it might be munging the JSON. I have tried wrapping this in stringify; meaning something like this: data: JSON.stringify( req.body ) (but the same error occurs).
callExternalAPI(
{
host: 'api_url',
path: '/addThing',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + new Buffer( auth_un + ':' + auth_pw ).toString('base64')
},
**data: JSON.stringify( req.body )**
}
)
I am testing this in postman, having the body be 'raw json' with headers as 'application/json'
the body is this:
{
"group_id": "45b62b61-62fa-4684-a058-db3ef284f699",
"person_id": "3b1c915c-3906-42cf-8084-f9a25179d6b2"
}
You should try to write the POST payload in the request body instead of passing it inside the options object:
function callExternalAPI( RequestOptions ) {
const { data, ...options } = RequestOptions;
return new Promise((resolve, reject) => {
const req = https.request(
options,
function(response) {
const { statusCode } = response;
if (statusCode >= 300) {
reject(
new Error( response.statusMessage )
);
}
const chunks = [];
response.on('data', (chunk) => {
chunks.push(chunk);
});
response.on('end', () => {
const result = Buffer.concat(chunks).toString();
resolve( JSON.parse(result) );
});
}
);
req.write(JSON.stringify(data));
req.end();
})
}
In express you must use bodyParser
At the top of the file when you initializing your express app add this lines
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
https://medium.com/#adamzerner/how-bodyparser-works-247897a93b90
Related
I am fetching IGDB api on server because I need to go through CORS. I am using async await connected to client side. Everything works fine but I need to pass query like '/?fields=cover.*,name;limit=50;' to https://api.igdb.com/v4/games from client side, not from server. When I am adding a query to client side, it's still showing the query only from server. How I can pass this query from client side? This is my code:
api/example.js
import Cors from "cors";
import initMiddleware from "../../components/init-middleware";
const cors = initMiddleware(
Cors({
methods: ['GET', 'POST', 'OPTIONS'],
})
)
const settings = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Client-ID': 'my_client-id',
'Authorization': 'Bearer my_authorization',
},
}
const remoteServerUrl = 'https://api.igdb.com/v4/games'
export default async function handler(req, res) {
await cors(req, res)
const response = await fetch(remoteServerUrl, settings);
const data = await response.json()
res.json(data)
}
client side
const settings = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Client-ID': 'my_client-id',
'Authorization': 'Bearer my_authorization',
},
const fetchData = async () => {
let query = '/api/example/'
const response = await fetch(query + HERE I WANT TO ADD QUERY, settings);
const data = await response.json();
}
Edit:
Status Code: 308 Permanent Redirect
initMiddleware
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
export default function initMiddleware(middleware) {
return (req, res) =>
new Promise((resolve, reject) => {
middleware(req, res, (result) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
I am trying to send a post request to a URL, I did this in python with the following code and it worked like a charm and I got a [Response <200>], but since I needed to use this in a website, I switched over to JS and tried to recreate the same functionality, but for some reason I'm getting a [Response <403>] even tho all my auth tokens and headers and everything is same as the python code.
Python Code -
url = "https://discord.com/api/v8/channels/801784356711956522/messages"
auth = ""
headers = {"Authorization": auth,
'Content-Type': 'application/json', 'referer': "https://discord.com/channels/801784356217421874/801784356711956522"}
payload = {'content': 'Test' , 'nounce': 802056256326991872, 'tts': False}
response = requests.post(url, data=json.dumps(payload), headers=headers)
print(response)
JavaScript Code -
onst url = "https://discord.com/api/v8/channels/801784356711956522/messages"
const auth = ""
const headers = {"Authorization": auth,
'Content-Type': 'application/json',
'referer': "https://discord.com/channels/801784356217421874/801784356711956522"}
const options = {
headers : headers,
}
const data = JSON.stringify({'content':"Test" , 'nounce': 802056256326991872, 'tts': false})
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
const req = https.request(options, (res) => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {
process.stdout.write(d)
})
})
req.on('error', (error) => {
console.error(error)
})
req.write(data)
req.end()
In your python code, you made a POST request but in JavaScript code, you made a GET request because you did not provide the method option.
It is specified in https.request options documentation:
method A string specifying the HTTP request method. Default:
'GET'.
To make POST request modify like this
const options = {
headers : headers,
method: "POST"
}
Also, you need to add URL since you did not provide hostname and path in the options.
const req = https.request(url, options, (res) => {
// ...
})
const querystring = require('querystring');
const https = require('https');
var postData = querystring.stringify({
'msg' : 'Hello World!'
});
var options = {
hostname: 'domain.com',
port: 443,
path: '/yow-path',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(postData);
req.end();
I am facing some issue while downloading file using node.js. I have scenario like my angular component is sending the file request. in my first node server I am doing the token validation and then redirecting to another node server where actually the execution happens. I am explaining my code below.
service.ts:
submitAndDownloadFile(formdata : any ){
const token = localStorage.getItem('token')
let headers = new HttpHeaders({
Authorization: 'Basic ' + token
})
const cecID = localStorage.getItem('cec');
const AppUrl = `${environment.nodeJsBaseUrl}:${environment.hostingNodeJsContainerPort}/convert-test-cases/${cecID}`;
return this.httpClient.post(AppUrl, formdata, { responseType: 'blob', observe : 'response', headers : headers});
}
Here I am sending the request to my first node.js server which code has given below.
app.js(first:port-8000):
router.post('/convert-test-cases/:id', middleware.auth, (req, res) => {
try{
let postRequestOptions = {
url: '',
method: 'POST',
json: true,
headers: {},
body: {},
};
postRequestOptions.url = 'http:localhost:9000/convert-test-cases';
postRequestOptions.headers = {
'Content-Type': 'application/json',
};
postRequestOptions.body = req.body;
request(postRequestOptions, async (error, response, pathList) => {
if(error) {
console.log('error', error);
}else{
res.send(pathList);
}
})
}catch(e){
responseObj = {
status: 'error',
msg: 'Error occurred while processing your request',
body: null
}
return res.send(responseObj);
}
})
Here I am doing the token validation using middleware.auth and sending same request to another node.js file which code is explained below.
app.js:(second-port-9000):
router.post('/convert-test-cases', async (req, res) => {
try{
let response = await ctcCtrl.convertTestCase(req.body, req.files);
if(response.status == 'success'){
res.set('Access-Control-Expose-Headers','*, Content-Disposition');
return res.download(response.fileName,response.fileName);
}else{
return res.send(response);
}
}catch(e){
responseObj = {
status: 'error',
msg: 'Error occurred while processing your request',
body: null
}
return res.send(responseObj);
}
})
Here only I am doing some execution and downloading the file. If I am connecting angular to node-9000 its working fine but my requirement is first I have to connect to port-8000 to some token validation and after that I have to send same req.body and re.file to app.js which is running in 9000 using request module. As per my code its not working at all.
I was using Azure Speech rest api. And i tried it on post man with a .wav file and it successfully return the result. However, when i call api from my node.js code. It always return Unsupported Audio Format even though i give the same audio file. Can anyone tell me what's the difference of them? Or what did Postman do to make it work?
Below is how i call speech api by node.js.
'use strict';
const request = require('request');
const subscriptionKey = 'MYSUBSCRIPTIONKEY';
const uriBase = 'https://westus.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US';
const options = {
uri: uriBase,
body: 'speech.wav',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key' : subscriptionKey,
'Transfer-Encoding': 'chunked',
'Expect': '100-continue',
'Content-type':'audio/wav; codec=audio/pcm; samplerate=16000'
}
};
request.post(options, (error, response, body) => {
if (error) {
console.log('Error: ', error);
return;
}
let jsonResponse = JSON.stringify(JSON.parse(body), null, ' ');
console.log('JSON Response\n');
console.log(jsonResponse);
});
You can try this
fs.readFile('/path/to/my/audiofile.wav', function (err, data) {
if (err) throw err;
var options = {
host: 'https://westus.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US',
method: 'POST',
headers: { 'Content-Type': 'audio/wav' }
};
var req = http.request(options, function(res) {
// Handle a successful response here...
});
req.on('error', function(e) {
// Handle an error response here...
});
// Write the audio data in the request body.
req.write(data);
req.end();
});
I'm using node.JS with request module.
My problem is, I need to authenticate the user on every request because the session is destroyed outside of the .then((response) => {}) block.
How is it possible to save the created session in a class for later use?
I tried out everything without success.
Here is a not working code snippet
login() {
const getLoginUrl = 'https://www.demourl.com/'
const postLoginUrl = 'https://www.demourl.com/account/login/'
rp({
url: getLoginUrl,
jar: this.cookieJar,
method: 'GET'
})
.then((body) => {
var csrftoken = this.cookieJar.getCookies(getLoginUrl)[1].toString().split('=')[1].split(';')[0];
var args = {
url: postLoginUrl,
json: true,
method: 'POST',
data: {
username: this.username,
password: this.password
},
headers: {
'method': 'POST',
'path': '/account/login/',
'cookie': 'csrftoken=' + csrftoken,
},
jar: this.cookieJar,
resolveWithFullResponse: true
}
rp(args)
.then((response) => {
//Here is a valid session
//But how can I use this session in different functions?
console.log('Post demourl.com/account/login success');
})
.catch((error) => {
console.log('Post demourl.com/account/login error: ', error);
});
})
.catch((error) => {
console.log('Get demourl.com error: ', error);
});
}
you should use this function as a middleware and then attach what ever you want to attach in to your req
try in you main script do
'use strict'
const express = require('express');
const login = require('./login');
const app = express()
app.use(login);// use this if you want all your routes to check login or put it in a specific route
app.get('/', (req,res)=>{
//this route is only for loged in users
});
const server = http.createServer(app).listen(process.env.PORT);
module.exports = app;
and in your login script
const login = (req, res, next) => {
const getLoginUrl = 'https://www.demourl.com/'
const postLoginUrl = 'https://www.demourl.com/account/login/'
rp({url: getLoginUrl, jar: this.cookieJar, method: 'GET'})
.then((body) => {
var csrftoken = this.cookieJar.getCookies(getLoginUrl)[1].toString().split('=')[1].split(';')[0];
var args = {
url: postLoginUrl,
json: true,
method: 'POST',
data: {
username: this.username,
password: this.password
},
headers: {
'method': 'POST',
'path': '/account/login/',
'cookie': 'csrftoken=' + csrftoken,
},
jar: this.cookieJar,
resolveWithFullResponse: true
}
rp(args)
.then((response) => {
res.loginResponse = response; // save the response for later use
console.log('Post demourl.com/account/login success');
next();
})
.catch((error) => {
console.log('Post demourl.com/account/login error: ', error);
return res.send(error) //send the error
});
})
.catch((error) => {
console.log('Get demourl.com error: ', error);
return res.send(error) //send the error
});
}
module.exports = login
I never see this.cookieJar being defined. Make sure it's initialized somewhere:
this.cookieJar = request.jar();
If you only use a single cookieJar in your application, you could also use Request's global cookie jar by setting the option jar to true:
// Either by setting it as the default
const request = require('request').defaults({jar: true});
// Or by setting it on each request
request('www.example.com', { jar: true });