With curl I can send GET-requests with url-encoded parameters as follows:
curl -G http://example.com --data-urlencode "key=val"
How do I do this with supertest / superagent? So far I've tried things like
const response = await request(app)
.get('/')
.type('application/x-www-form-urlencoded')
.send({ key: 'val' });
Try to specify form in type like below:
const response = await request(app)
.get('/')
.type('form') // change into `form`
.send({ key: 'val' });
Ref:
https://visionmedia.github.io/superagent/
You can make use of it this way:
const response = await request(app)
.get('/')
.send('key=val&key2=val2&key3=val3');
Documentation: https://www.npmjs.com/package/supertest (Search 'x-www-form-urlencoded upload')
Related
I want to send a post request that contain a form data, i want to do that from nodejs to another external api, i don't have the front-end to send the formData, so all i have a javascript object that has keys and values, so how can i do that?, when i tried sending a normal object, i didn't get the right api response, but when i sent it with a POSTMAN client, i got the correct response.
If you have a correct result in Postman, it's interesting to use the code generator in the same tools to have the desired code :). The button "</>" is on the right bar of the screen.
Here is the code generated from the tool :
var axios = require('axios');
var FormData = require('form-data');
var data = new FormData();
data.append('data', 'asldkfjalsdkjf');
var config = {
method: 'post',
url: 'https://some-domain.com/formdata',
headers: {
...data.getHeaders()
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
It's cool, isn't it?
One more thing, you have many options from NodeJS to C#, PHP.. :)
So you want to make a post request with nodejs? In order to do so, you can use the axios library and send the data in the following way.
const axios = require('axios');
let formData = new FormData();
formData.append('x': 'some test data');
axios({
method: 'post',
url: 'https://stackoverflow.com/posts/67709177',
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
})
.then(res => {
console.log(`statusCode: ${res.statusCode}`)
console.log(res)
})
.catch(error => {
console.error(error)
})
You can install axios using this command.
npm i axios
I'm trying to convert this curl command to a node fetch request.
curl -X POST http://localhost:7200/test -H 'Content-Type: multipart/form-data' -F "config=#test.ttl"
What I have so far sends the file as formdata via a POST request, just like the curl request. However, I am not sure how to include the config=#test.ttl in the request. I tried adding it to a headers object and I got back invalid characters errors so I am not sure where to put it. When I run the request the way it is below. I get back 'Required request part 'config' is not present' so it is definitely required to put the config somewhere.
const fetch = require("node-fetch");
const FormData = require('form-data');
const form = new FormData();
form.append('test.ttl', 1);
fetch('http://localhost:7200/test', {
method: 'POST',
body: form
})
.then(res => res.json())
.then(json => console.log(json));
Thanks
Thanks #bato3!
Creating a readStream for the data in the file worked. Posting the working code below if anyone wants to reference it in the future.
const fetch = require("node-fetch");
const fs = require('fs');
const FormData = require('form-data');
const form = new FormData();
form.append('config', fs.createReadStream('test.ttl'));
fetch('http://localhost:7200/test', {
method: 'POST',
body: form
})
.then(res => res.text())
.then(body => console.log(body));
I'm trying to get/read the response from a POST request made in Angular 7, if I set the API to return "text" everything is OK, but when i make the response a JSON the response body on Angular is null.
If I try the API on Postman I get full response JSON body.
I tried changing response method on Express API (from the simple res.json() to the "old" way of declaring Content-Type and sending it with res.end()), nothing changed.
The response code I'm using on backend:
res.status(200).json({
success: true,
token: token
})
What I also tried:
res.writeHead(200, { 'Content-Type': 'application/json' })
var json = JSON.stringify({
success: true,
token: token
})
res.end(json)
The service I'm using on Angular:
login(username: string, password: string): Observable<any> {
let body = {username: username, password: password};
let headers = new HttpHeaders();
headers.set('Content-Type', 'application/json');
return this.http.post(this.baseUrl + "/login/sign-in", body, {headers: headers, responseType: 'json', observe: 'response'});
}
The call to that service:
this.api.login("admin", "password").subscribe(result => {
console.log(result);
})
On Postman I get this result:
On Angular I get this (JSON):
On Angular I get this (TEXT):
Edit:
If I add anything before the JSON on the Express app, the body is no more null:
res.writeHead(200, { 'Content-Type': 'application/json' })
var json = JSON.stringify({
success: true,
token: token
})
res.end('some_char' + json)
The result (of course the response goes in error):
Edit 2:
I'm also trying (with no luck) with this simple version of the endpoint:
const express = require('express')
const app = express()
app.post('/login/sign-in', (req, res) => res.json({ value: 1 }))
app.listen(3000, () => {
console.log('App running on port 3000.')
})
Solution:
It was all a CORS problem, I added this to the backend and everything is working fine:
app.use(cors())
Spent a few minutes trying to find out why the body would be empty,
In my case, I had "mode":"no-cors" set in my fetch() options, therefore the returned value from the server would appear as "opaque"
redux fetch body is not use with no cors mode
I hope this can help !
Here is what I want to do:
When an item is created within a certain app, a webhook sends the data contained within that item to an external application
Same thing happens what an item is edited or deleted
Naturally, I'm using the native Podio webhooks. I made a hook for item.create, item.update, and item.delete. The external URLs are correct and clicking the "verify" button sends a successful call to the external app.
However, none of the actions actually work. I've created, changed, and deleted items - nothing. Only clicking the "verify" button causes any communication with the external app.
What are some common reasons why this might not be working?
Have you activated that webhook? From Podio documentation https://developers.podio.com/examples/webhooks
Before your webhooks becomes active the URL must be verified. Immediately after the webhooks is created a hook.verify notification is sent to the URL endpoint. The endpoint must then return the code to the validation operation. Events will only be sent to the endpoint after a completed verification.
Example with command line curl:
Please remember to inject correct access_token, hook_id and verification_code
curl -H "Content-Type: application/json" -H "Authorization: OAuth2
[access_token]" -X POST -d "{'code': [verification_code]}"
https://api.podio.com/hook/[hook_id]/verify/validate
Here's how I managed to verify my Node.JS webhook endpoint and make it active using Express.JS:
const express = require("express");
const app = express();
const https = require('https');
app.use(express.urlencoded());
app.use(express.json());
app.post('/', function(request, response){
console.log( "POST", request.body);
// VERIFICATION >>
const postData = JSON.stringify({ code: request.body.code });
const options = {
hostname: 'api.podio.com',
port: 443,
path: '/hook/' + request.body.hook_id + '/verify/validate',
method: 'POST',
headers: {
'content-type': 'application/json',
'accept': 'application/json'
}
};
const req = https.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.write(postData);
req.end();
// << VERIFICATION
response.send(request.body);
});
app.listen(443);
I am testing a Node.js API with supertest, and I cannot explain why the res.body object superset returns is empty. The data shows up in the res.text object, but not res.body, any idea how to fix this?
I am using Express and body-parser:
app.use(bodyParser.json());
app.use(bodyParser.json({ type: jsonMimeType }));
app.use(bodyParser.urlencoded({ extended: true }));
Here is the API method I am testing:
app.get(apiPath + '/menu', function(req, res) {
var expiration = getExpiration();
res.set({
'Content-Type': jsonMimeType,
'Content-Length': jsonTestData.length,
'Last-Modified': new Date(),
'Expires': expiration,
'ETag': null
});
res.json({ items: jsonTestData });
}
Here are the tests I am executing against this API method:
describe('GET /menu', function() {
describe('HTTP headers', function() {
it('responds with the right MIME type', function(done) {
request(app)
.get(apiPath + '/menu')
.set('Accept', 'application/vnd.burgers.api+json')
.expect('Content-Type', 'application/vnd.burgers.api+json; charset=utf-8')
.expect(200, done);
});
it('responds with the right expiration date', function(done) {
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0,0,0,0);
request(app)
.get(apiPath + '/menu')
.set('Accept', 'application/vnd.burgers.api+json; charset=utf-8')
.expect('Expires', tomorrow.toUTCString())
.expect(200, done);
});
it('responds with menu items', function(done) {
request(app)
.get(apiPath + '/menu')
.set('Accept', 'application/vnd.burgers.api+json; charset=utf-8')
.expect(200)
.expect(function (res) {
console.log(res);
res.body.items.length.should.be.above(0);
})
.end(done);
});
});
});
The failure I receive:
1) GET /menu HTTP headers responds with menu items:
TypeError: Cannot read property 'length' of undefined
at /Users/brian/Development/demos/burgers/menu/test/MenuApiTest.js:42:25
at Test.assert (/Users/brian/Development/demos/burgers/menu/node_modules/supertest/lib/test.js:213:13)
at Server.assert (/Users/brian/Development/demos/burgers/menu/node_modules/supertest/lib/test.js:132:12)
at Server.g (events.js:180:16)
at Server.emit (events.js:92:17)
at net.js:1276:10
at process._tickDomainCallback (node.js:463:13)
And finally, here is an excerpt of the result of console.log(res):
...
text: '{"items":[{"id":"1","name":"cheeseburger","price":3},{"id":"2","name":"hamburger","price":2.5},{"id":"3","name":"veggie burger","price":3},{"id":"4","name":"large fries","price":2},{"id":"5","name":"medium fries","price":1.5},{"id":"6","name":"small fries","price":1},{"id":"7","name":"large drink","price":2.5},{"id":"8","name":"medium drink","price":2},{"id":"9","name":"small drink","price":1}]}',
body: {},
...
Based on the following test you are expecting 'application/vnd.burgers.api+json; charset=utf-8' as the Content-Type:
request(app)
.get(apiPath + '/menu')
.set('Accept', 'application/vnd.burgers.api+json')
.expect('Content-Type', 'application/vnd.burgers.api+json; charset=utf-8')
.expect(200, done);
This express route also shows you setting the header to some custom value, jsonMimeType:
app.get(apiPath + '/menu', function(req, res) {
var expiration = getExpiration();
res.set({
'Content-Type': jsonMimeType,
'Content-Length': jsonTestData.length,
'Last-Modified': new Date(),
'Expires': expiration,
'ETag': null
});
res.json({ items: jsonTestData });
}
If this is the case, supertest isnt going to parse that JSON automatically for you. The content-type header must start with the string 'application/json'. If you cant make that happen, then you will have to use the JSON.parse function yourself to convert that text string to an object.
supertest uses this file to determine if you are sending json or not. Under the hood, supertest actually starts up your express server, makes the one request via HTTP, and quickly shuts it down. After that HTTP handoff, the client side (which is basically superagent) of that HTTP request doesnt know anything about your server configuration with regard to 'application/vnd.burgers.api+json; charset=utf-8'. All it know is what its told via headers, in this case, content-type.
Also, I did try your custom header on my machine and I also got an empty body.
Edit: updated table link as stated in the comments
This is old, but it helped me so thought I might share some knowledge.
Working off of mattr example, I found that that information was actually in the res.text, not the res.body.
I ended up adding some special handling for:
if(res.headers['content-type'] == 'myUniqueContentType' && res.body===undefined){
res.body = JSON.parse(res.text);
}
My problem was that the .set() method set the request headers, whereas .send() will set the request body with the json data you specify.
request("/localhost:3000")
.post("/test")
.type("json")
.set({color: "red"}) //this does nothing!
.expect(200)
.end(function(res) {
done();
});
The fix:
request("/localhost:3000")
.post("/test")
.type("json")
.send({color: "red"}) //fixed!
.expect(200)
.end(function(res) {
done();
});
Make Sure your supertest request has the 'accept: application/json' header set
I was noticing I only had my result as text instead of json until I set the request header to accept: application/json.
request(myServerObject)
.get("/myJSONurl")
.set("accept", "application/json") // This is the line you should set
.end(function() {
if (err) throw err
console.log(res.body) // should have a value
})
add
app.use(bodyParser.json({ type: 'application/*+json' }))
this will accept all json content types :-)