I am trying to call a REST API from node using node-rest-client. In the event that my call returns an error, I want to catch the error and report it to the caller.
I am trying this with Postman, unfortunately this only works once. When I press send the second time, my node.js program crashes with the error "Can't set headers after they are sent."
I am new at node.js, so any help is highly appreciated!
//app stuff
const client_id = "x";
const client_secret = "y";
const callback = "https://myurl;
// Basic Setup
var http = require('http'),
express = require('express'),
mysql = require('mysql'),
parser = require('body-parser'),
Client = require('node-rest-client').Client;
var client = new Client();
// Setup express
var app = express();
app.use(parser.json());
app.use(parser.urlencoded({ extended: true }));
app.set('port', process.env.PORT || 5000);
// Set default route
app.get('/', function (req, res) {
res.send('<html><body><p>Welcome to Bank API Wrapper</p></body></html>');
});
app.post('/authorize', function (req,res) {
var response = [];
if (typeof req.body.code !== 'undefined' && typeof req.body.state !== 'undefined' ){
var code = req.body.code, state = req.body.state;
//conversion to base64 because citi api wants it this way
var authorization = "Basic " + Buffer.from(client_id + ":" + client_secret).toString('base64');
var args = {
data:{"grant_type":"authorization_code","code":code,"redirect_uri":callback},
headers:{"Authorization":authorization,"Content-Type":"application/x-www-form-urlencoded"}
};
//get access and refresh token
client.post("https://sandbox.apihub.citi.com/gcb/api/authCode/oauth2/token/sg/gcb", args, function (citidata, citiresponse) {
//console.log(citidata);
//console.log(citiresponse);
});
client.on('error', function (err) {
response.push({'result' : 'error', 'msg' : 'unauthorized access'});
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(response));
});
}
else {
response.push({'result' : 'error', 'msg' : 'Please fill required details'});
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(response));
}
});
// Create server
http.createServer(app).listen(app.get('port'), function(){
console.log('Server listening on port ' + app.get('port'));
});
You've got this:
client.on('error', function (err) {
This register an error handler on the client but it never gets removed. The client is shared between requests so any errors on subsequent requests will still fire the old error handlers.
Instead you can listen for an error on the request. Something like this:
var request = client.post("...
request.on('error', function(err) {
// handle error
});
See https://www.npmjs.com/package/node-rest-client#error-handling
Related
The problem I'm having is, the content that I try to send in my post request to the server doesn't get sent, but the request works.
Here's the code for the client:
$("#searchBtn").click(function(e){
try{
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "/search/searchRequest", true);
console.log(($("#searchedSymptoms").val())) // gets posted in the console correctly
xhttp.setRequestHeader("Content-type", "text/plain"); // doesn't work without it either
xhttp.send($("#searchedSymptoms").val());
//xhttp.send(JSON.stringify($("#searchedSymptoms").val())); // doesn't work either
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
console.log(xhttp.responseText); // gets the correct response from server
}
else{
console.log(xhttp.responseText);
}
};
}
catch(err){
console.log(err);
}
});
And here's the server-side code:
var express = require("express");
var router = express.Router();
router.post("/searchRequest", function(req, res, next){
console.log("must get the client request:");
console.log(req.body);
//console.log(JSON.stringify(req.body)); // doesn't work either
});
In the server, what get's outputed to the console is this:
{}
Any thoughts on what I'm doing wrong ?
You need to use a text body-parser, Express won't do it by default, here's an example, using pretty much the same server side code you are:
"use strict";
var express = require("express");
var router = express.Router();
var bodyParser = require("body-parser");
router.post("/searchRequest", function(req, res, next){
console.log("must get the client request:");
console.log("SearchRequest: " + req.body);
res.end('ok', 200);
});
var port = 8081;
var app = express();
app.use(bodyParser.text());
app.use(router);
app.listen(port);
console.log("Express listening on port " + port);
You can configure the way the text body parser operates exactly by using the guide here:
https://www.npmjs.com/package/body-parser#bodyparsertextoptions
I currently have a node.js script sitting on Azure that gets a file (via a download URL link to that file) and base64 encodes it, and then sends this base64 encoded file back to the request source. The problem I am running into is performance based. The script below, in some instances, is timing out a separate application by having a run time over 30 seconds. The file in question on one of these timeouts was under a MB in size. Any ideas?
The script:
const express = require('express');
const bodyParser = require('body-parser');
const https = require('https');
const fs = require('fs');
const request = require('request');
const util = require('util');
const port = process.env.PORT || 3000;
var app = express();
app.use(bodyParser.json());
app.post('/base64file', (req, res) => {
var fileURL = req.body.fileURL;
var listenerToken = req.body.listenerToken;
var testingData = {
fileURL: fileURL,
listenerToken: listenerToken
};
/*
Make sure correct token is used to access endpoint..
*/
if( listenerToken !== <removedforprivacy> ) {
res.status(401);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ error: 'You are not authorized'}));
} else if ( !fileURL ){
res.status(400);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ error: 'The request could not be understood by the server due to malformed syntax.'}));
} else {
https.get(fileURL, function(response) {
var data = [];
response.on('data', function(chunk) {
data.push(chunk);
}).on('end', function() {
//build the base64 endoded file
var buffer = Buffer.concat(data).toString('base64');
//data to return
var returnData = {
base64File: buffer
};
if( buffer.length > 0 ) {
res.setHeader('Content-Type', 'application/json');
res.status(200);
res.send(JSON.stringify(returnData));
} else {
res.setHeader('Content-Type', 'application/json');
res.status(404);
res.send(JSON.stringify({ error: 'File URL not found.'}));
}
});
});
}
});
app.listen(port, () => {
console.log('Server is up and running ' + port);
});
One idea: you are missing error handling.
If you get an error on the https.get(), you will just never send a response and the original request will timeout.
I want to start a proxy server with node.js so that it serves requests preprended with "/wps_proxy/wps_proxy?url=". I want it so I can use the wps-js library of north52 (check the installation tips) . I have already a server where I run my application.
What I did try until now is :
the server.js file
var express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var path = require("path");
var app = express();
app.use(express.static(__dirname + '/' + 'public'));
var urlencodedParser = bodyParser.urlencoded({ extended: false });
//****** this is my try ******************************
app.get('/wps_proxy/wps_proxy',function (req,res){
res.sendfile(__dirname + '/' + 'public/wps_proxy/wps-js/target/wps-js-0.1.2-SNAPSHOT/example.html');
if(req.query !== undefined){//because it enters sometimes without url
var http = require('http');
//Options to be used by request
var options = {
host:"geostatistics.demo.52north.org",//fixed given data
port:"80",
path:"/wps/WebProcessingService"
};
var callback = function(response){
var dat = "";
response.on("data",function(data){
dat+=data;
});
response.on("end", function(){
res.end(dat)
})
};
//Make the request
var req = http.request(options,callback);
req.end()
}
})
var ipaddress = process.env.OPENSHIFT_NODEJS_IP||'127.0.0.1';
var port = process.env.OPENSHIFT_NODEJS_PORT || 8080;
app.set('port', port);
app.listen(app.get('port'),ipaddress, function() {
console.log( 'Server started on port ' + app.get('port'))
})
//***************************************
but its not working.. I think that the data are not sent back correctly..
This is a live example of what I want to do.. http://geoprocessing.demo.52north.org/wps-js-0.1.1/
and this is a live example of my application (check the console for errors) http://gws-hydris.rhcloud.com/wps_proxy/wps_proxy
I did find my answer from this post How to create a simple http proxy in node.js? so the way i solve it was:
app.get('/wps_proxy/wps_proxy',function (req,res){
var queryData = url.parse(req.url, true).query;
if (queryData.url) {
request({
url: queryData.url
}).on('error', function(e) {
res.end(e);
}).pipe(res);
}
else {
res.end("no url found");
}
})
Following is my server file. I am making 2 calls, one post and one get. It works fine at times. But gives an error of : Can't set headers after they are sent. Does this have anything to do with my client side code?
server.js
var express = require('express')
var mongoose = require('mongoose')
var path = require('path')
var bodyParser = require("body-parser")
var cors = require("cors")
var app = express()
var port = process.env.PORT || 3000
var Url = require("./data/url-schema");
//Express request pipeline
app.use(express.static(path.join(__dirname,"../client")))
app.use(bodyParser.json())
app.use(cors());
/*
Your server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /dashboard your web server will get a request to /dashboard. You will need it to handle that URL and include your JavaScript application in the response.
*/
app.get('*', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/:code', function(req, res) {
console.log("reg", req.params.code)
Url.findOne({code:req.params.code}, function(err, data){
console.log("data", data)
if(data)
res.redirect(302, data.longUrl)
else
res.end()
})
})
app.post('/addUrl', function (req, res, next) {
console.log("on create");
Url.findOne({longUrl:req.body.longUrl}, function(err, data) {
if (err)
res.send(err);
else if(data) {
console.log("already exists",data)
res.send("http://localhost:3000/"+data.code);
} else {
var url = new Url({
code : Utility.randomString(6,"abcdefghijklm"),
longUrl : req.body.longUrl
});
console.log("in last else data created",url)
url.save(function (err, data) {
console.log(data)
if (err)
res.send(err);
else
res.send("http://localhost:3000/"+data.code);
});
}
});
})
app.listen(port, function () {
console.log('Example app listening on port 3000!')
});
// Connect to our mongo database
mongoose.connect('mongodb://localhost/shortUrl');
I get the Following error
error
_http_outgoing.js:335
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:335:11)
at ServerResponse.header (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:718:10)
at ServerResponse.location (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:835:8)
at ServerResponse.redirect (/opt/lampp/htdocs/url-shortener/node_modules/express/lib/response.js:874:8)
at Query.<anonymous> (/opt/lampp/htdocs/url-shortener/server/server.js:30:8)
at /opt/lampp/htdocs/url-shortener/node_modules/mongoose/node_modules/kareem/index.js:177:19
at /opt/lampp/htdocs/url-shortener/node_modules/mongoose/node_modules/kareem/index.js:109:16
at process._tickCallback (node.js:355:11)
From the execution order, in * route handler, the body is being assigned to the response and then in /:code, the response code 302 is being added, where Location header is also added, hence the error. Any header must be added before the body to the response.
To solve this problem, simply change the order of the two GET statements.
Finally found the solution:
var express = require('express')
var mongoose = require('mongoose')
var path = require('path')
var bodyParser = require("body-parser")
var app = express()
var port = process.env.PORT || 3000
var Url = require("./data/url-schema")
var Utility = require("./utility")
//Express request pipeline
app.use(express.static(path.join(__dirname,"../client")))
app.use(bodyParser.json())
/*
Your server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /dashboard your web server will get a request to /dashboard. You will need it to handle that URL and include your JavaScript application in the response.
*/
app.get('/dashboard', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/about', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
app.get('/:code', function(req, res) {
Url.findOne({code:req.params.code}, function(err, data){
if(data){
res.redirect(302, data.longUrl)
}
})
})
app.post('/addUrl', function (req, res, next) {
Url.findOne({longUrl:req.body.longUrl}, function(err, data) {
if (err){
res.send(err)
}
else if(data) {
res.send("http://localhost:3000/"+data.code);
} else {
var newCode = getCode()
checkCode(newCode)
.then(function(data){
var url = new Url({
code : data,
longUrl : req.body.longUrl
});
url.save(function (err, data) {
if (err)
res.send(err);
else
res.send("http://localhost:3000/"+data.code);
});
})
}
});
})
app.listen(port, function () {
console.log('Example app listening on port 3000!')
});
// Connect to our mongo database
mongoose.connect('mongodb://localhost/shortUrl');
//Generate a random code
function getCode() {
return Utility.randomString(6,"abcdefghijklmnopqrstuvwxyz")
}
//Check if the code is unique
function checkCode(code) {
return new Promise(function (resolve, reject){
Url.findOne({code:code}, function(err, data) {
if(err === null){
resolve(code)
}else if(data){
saveUrlCode(getCode())
}
})
})
}
My earlier route which was :
app.get('*', function (request, response, next){
response.sendFile(path.resolve(__dirname, '../client', 'index.html'))
next()
})
The get route was getting executed twice on account of the above call and the
app.get(":/code") call.
So I had to handle the routes properly which I have done by handling the dashboard and about routes separately instead of using the "*" route.
I use the following code to accept the user sms from my android app and send back the result to the user after making specified get request to some site.the expected output that the user should get is "thanks for your message"+[the response of get request]..what i get is "Thanks for your message undefined"it seems that my variable "body" doesnt get initialized with the GET response.please help
var express = require('express');
var app = express();
app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/public'));
app.get('/', function(request, response) {
response.send('Hello Cruel World!');
});
var bodyParser = require('body-parser');
var WEBHOOK_SECRET = "62DZWMCCFFHTTQ44CG3WUQ94CTT7GAAN";
app.post('/telerivet/webhook',
bodyParser.urlencoded({ extended: true }),
function(req, res) {
var secret = req.body.secret;
if (secret !== WEBHOOK_SECRET) {
res.status(403).end();
return;
}
if (req.body.event == 'incoming_message') {
var content = req.body.content;
var from_number = req.body.from_number;
var phone_id = req.body.phone_id;
var request = require("request");
var body;
request("http://www.google.com", function(error, response, data) {
body = data;
});
// do something with the message, e.g. send an autoreply
res.json({
messages: [
{ content: "Thanks for your message! " + body}
]
});
}
res.status(200).end();
}
);
app.listen(app.get('port'), function() {
console.log('Node app is running on port', app.get('port'));
});
please help me to resolve the problem..the answer posted here doesnt seems working http://goo.gl/GYgd6Z,help by taking my code specified here as example...please
The res.json line will execute before the callback from request, so body won't be populated - change like this:
request("http://www.google.com", function(error, response, data) {
// do something with the message, e.g. send an autoreply
res.json({
messages: [
{ content: "Thanks for your message! " + data}
]
});
res.status(200).end();
});
This ensures that the res.json is executed after the response from request().