Express.js: API URL and res.json, using request package from npm - javascript

I'm at wits end trying to figure out how to get my API to return a valid JSON object.
I'm currently using the following code to send a response:
res.json({ foo: 'bar', secret: 'code' });
I have also tried:
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ foo: 'bar', secret: 'code' }));
Both of the above methods work when I visit the API URL in browser. I get the expected:
{
"foo": "bar",
"secret": "code"
}
On the other hand, I'm having trouble parsing the JSON response from URL (the example below uses the request package from npm):
request({
url: API-URL,
json: true
}, function (error, response, body) {
if (!error) {
console.log(body);
} else {
console.log(error);
}
})
Using the above code, I get the following error response:
{
"code": "ENOTFOUND",
"errno": "ENOTFOUND",
"syscall": "getaddrinfo",
"hostname": <<Redacted>>,
"host": <<Redacted>>,
"port": 443
}
I've tried plugging in third-party API URLs and they work just fine (ie: Google Maps, and OpenWeatherAPI) so I'm pretty sure that I'm using request properly. I'm guessing there is a step I missed in setting up the API (or I'm doing something wrong)?
Testing my API URL in https://jsonformatter.curiousconcept.com/, the formatted JSON object shows up but I also get the following regardless of how I set the response header and encoding:
Error: Invalid encoding, expecting UTF-8, UTF-16 or UTF-32.[Code 29, Structure 0]
Any suggestions would be greatly appreciated!
Edit:
The above is in a live web app and Express works properly otherwise (everything else in the application works fine, the JSON response just isn't playing nicely with request or jQuery.

Related

How to read parameters of Azure function HTTP request and respond with a file from blob storage?

I'm building an Azure Function app and have had some issues trying to proxy requests (for data capture purposes) and still respond with the requested files. Essentially what I'm trying to accomplish is:
Client requests file via GET parameter on an Azure Function endpoint (not its blob storage location)
Function logs some metadata to table storage (e.g. IP address, timestamp, file name, etc.)
Function locates the desired file in blob storage and forwards it to the client as if Step 2 didn't occur
I've tried an approach using Q (outlined here) with no luck, and I haven't been able to narrow the issue down (beyond standard 500 errors).
The tutorial above basically goes as follows:
const rawFile = await q.nfcall(fs.readFile, blobUrl);
const fileBuffer = Buffer.from(rawFile, ‘base64’);
context.res = {
status: 202,
body: fileBuffer,
headers: {
"Content-Disposition": "attachment; examplefile.mp3;"
}
};
context.done();
I've hit a bit of a wall and I'm struggling to find a solution to what I thought would be a common problem (i.e. simply logging download metadata to a table). I'm new to Azure and so far I'm finding things a bit tedious... Is there a simple way of doing this?
Edit: Based on the response I get from context.bindings.myFile.length I think I've been able to retrieve a file from blob storage, but I haven't yet been able to send it back in the response. I've tried the following:
context.res = {
status: 202,
body: context.bindings.myFile,
headers: {
'Content-Type': 'audio/mpeg',
'Content-Disposition': 'attachment;filename=' + fileName,
'Content-Length': context.bindings.myFile.length
}
};
Edit 2: I think this is pretty much solved - I overlooked the methods and route in the HTTP input part of the answer below. Looks like I'm able to dynamically retrieve blobs based on the GET request and use them as inputs, and I've been able to get them sent back to the client as well. My HTTP response now looks like this:
context.res = {
status: 200,
headers: {
'Content-Length': context.bindings.myFile.length,
'Content-Type': 'audio/mpeg'
},
body: context.bindings.myFile,
isRaw: true
};
Let me paraphrase what you asked:
You want just to retrieve blob storage object upon http request.
I think it is worth looking into bindings. They simplify the integration with other Azure services - Storage Accounts, Service Bus, Twilio - and Function Apps.
In your case it should be an input binding for blob storage. As one way to achieve it you need to customize your route in the http trigger part of function.json as follows: file/{fileName}. Then you use fileName in input binding definition in the same function.json.
I think function.json should like this:
{
"bindings": [
{
"type": "httpTrigger",
"name": "req",
"direction": "in",
"methods": [ "get" ],
"route": "file/{fileName}"
},
{
"name": "myFile",
"type": "blob",
"path": "your-container-name/{fileName}",
"connection": "MyStorageConnectionAppSetting",
"direction": "in"
},
{
"type": "http",
"name": "res",
"direction": "out"
}
]
}
With your index.js as follows:
module.exports = function(context, req) {
context.log('Node.js Queue trigger function processed', context.bindings.myFile);
const fileToReturn = context.bindings.myFile;
// you return it here with context.res = ...
context.done();
};
You also superficially mentioned the logging that is not part of your question but is mentioned. I recommend looking into Azure Application Insights. It might serve the purpose.

paypal express checkout - 400 bad request

I'm using this instruction https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/advanced-integration/ to set up paypal. The only different is that I need to send data to CREATE_PAYMENT_URL endpoint, so I transfer it as second parameter
paypal.request.post(
CREATE_PAYMENT_URL,
JSON.stringify([{"name": "test", "price": 10}]),
{ headers: { 'Content-Type': 'application/json' } }
)
after that I'm getting 400 error
{"success":false,"errors":{"statuscode":400,"message":"Invalid json message received"}}
Backend doesn't matter, cause it's never reached. Also I'm almost sure it worked fine few days ago
Any ideas?
Fiddler screen
Try setting the second param to null, and send an options object as the third param with an object named json in the third with a plain JS object inside.
paypal.request.post(PAYMENT_URL, null, {
json : {
name:'test',
price:10
}
})
It's not in the documentation, I was having similar issues but dug around in the source code to get it to work.

Node.js + Sending API Request to Third Party Service

I have a Node console app. In my app, I am trying to send some data to a third-party web service. I can successfully add data via the third-party web service via POSTMAN. In my Postman, I have a request setup like this:
Headers
api-key
{my key}
Content-Type
application/json
Body
{
"value": [
{
"#operation": "send",
"id":"1",
"name":"Hello",
"position": { "type": "Point", "coordinates": [90.000000, 0.000000] },
"tags":["january", "friends"]
}
]
}
I am now trying to replicate sending this data via Node. In an attempt to do this, I've written the following:
var ops = {
host: 'example.com',
path: '/api/upload?version=2',
method: 'POST',
headers: {
'api-key':'[my key]',
'Content-Type': 'application/json'
}
};
var r = https.request(ops, (res) => {
res.on('data', (d) => {
console.log(res.statusCode);
console.log(res.statusMessage);
});
});
var data = {
"value": [
req.body.record // req is passed in via express
]
};
console.log(data);
r.write(JSON.stringify(data));
r.end();
When the line console.log(data); is executed, I see the following in the console window:
{ value:
[ { '#operation': 'send',
id: '1',
name: 'Hello',
position: [Object],
tags:[Object]
} ] }
I'm not sure if this is a printing problem or an actual problem. Either way, my real issue is that when I send my request, I see the following also printed in the console window:
400
Bad Request
That data looks correct. I don't understand why I'm getting a 400 when I try to send from Node. Yet, it works just fine from Postman. Does it have to do with the quotation marks? I thought JSON.stringify handled that.
What am I doing wrong?
Honestly, unless you have a really good reason not to, just use something like request. It's way easier/cleaner than trying to build it yourself.
I can't answer any more specifically without knowing what the API you're talking to is expecting.
Also, in your res.on('data'), just console.log(req); and see what else is hiding in there; it might help you solve this with your existing code.
I would try
console.log(JSON.stringify(data));
Instead of
console.log(data);
and then decide what is wrong. This will give me full data in way it is sent to server.
Update:
I have made quick experiment and received following output:
{"value":[{"#operation":"send","id":"1","name":"Hello","position":{"type":"Point","coordinates":[90,0]},"tags":["january","friends"]}]}
The difference with the original JSON is in way how the coordinates are sent to the web service (as integers), which can be your problem.

Send Array in post request using node.js using application/x-www-form-urlencoded

I tried to send post request to API and the post parameters should be array,
this is how to send it in cURL
curl http://localhost:3000/check_amounts
-d amounts[]=15 \
-d amounts[]=30
I tried to do that in Node.js using request module
request.post('http://localhost:3000/check_amounts', {
form: {
'amounts[]': 15 ,
'amounts[]': 30
}
}, function(error, response, body) {
console.log(body)
res.json(body);
});
but the second amount override the first one and the API gets the result as following: amounts = [30]
Then I tried to send it in different way
request.post('http://localhost:3000/check_amounts', {
form: {
'amounts[]': [ 15 , 30]
}
}, function(error, response, body) {
console.log(body)
res.json(body);
});
but the result was not as an expected amounts = [{"0":15},{"1":30}]
Note : the header should contains 'Content-Type': 'application/x-www-form-urlencoded' not 'application/json'
Does any one have solution to this problem?
It's quite easy if you read the manual of request. All you should do is replace the form by querystring rather than object, in your case this should be:
amounts=15&amounts=30
the only thing I'm not sure is the above expression works in your web server. As I know it works well in java struts. So if not you may try
amounts[]=15&amounts[]=30 instead. Hope it help.

Data is not passed from AngularJS controller to NodeJS server

I am attempting to pass some values from my client-side AngularJS script to a server-side NodeJS script. I set up the POST request like so:
$scope.addUser = function() {
console.log($.param($scope.user));
$http({
method: 'POST',
url: '/addUser',
data: $.param($scope.user),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).
success( function(response) {
console.log("success");
}).
error( function(response) {
console.log("error");
});
};
The $scope.user variable is { name: "john", email: "doe" }, and evaluates to name=john&email=doe when passed through $.param($scope.user). I originally thought the problem was the content-type of the request, which was originally a JSON object. After reading about similar problems I changed the content-type to x-www-form-urlencoded, but still could not grab the data from the POST request.
Here is the server-side NodeJS script that is hit with the POST request:
app.post('/addUser', function(req, res) {
console.log(req.params);
});
I know the server-side script is being reached, as I can print out data such as req.method, but attempting to print req.params results in just { }.
Why are my POST parameters not going through?
Request bodies are not saved to req.params. You need to add a middleware to parse the request body for you. req.params is for key=value pairs supplied as part of the URL (e.g. POSTing to "/foo/bar?baz=bla" would result in req.params.baz === 'bla').
Some example solutions:
body-parser - parses only application/json and application/x-www-form-urlencoded request bodies.
formidable - parses application/json, application/x-www-form-urlencoded, and multipart/form-data. This is what was used in the body parser in Express 3. I'm not sure if there is an "official" Express middleware for it for Express 4.
busboy - parses application/x-www-form-urlencoded and multipart/form-data. It does not save files to disk itself, but instead presents files as readable streams. The API differs from formidable(/body parser from Express 3). There are a few Express middleware available for busboy:
connect-busboy - a thin wrapper that merely sets up a Busboy instance on your req. You can set it to automatically start parsing the request, or you can pipe the request manually to req.busboy when you want to start.
multer - provides an interface more similar to the Express 3 body parser middleware (with req.body and req.files set).
reformed - a new module that provides a layer on top of Busboy to provide mechanisms similar to formidable (e.g. saving uploaded files to disk) but also other features such as field validation.
Since you are using express.js your POST fields are received as part of the body not the URL so you need use body instead of params:
app.post('/addUser', function(req, res) {
console.log(req.body);//also possible req.body.name | req.body.email
});
How about trying a simpler POST something like this (for testing purposes only):
$http.post('/addUser',{ "name": "john", "email": "doe" }).success(function(response) {
console.log("success");
}).error(function(err){
console.log("failure")
});
Please note that Params are used as URL parameters; something like this:
app.get('/addUser/:userID', function(req, res) {
console.log(req.params.userID);
});

Categories