I'm trying to use frisby.js to specify an API test for an endpoint that returns a plain text response to a valid POST request. However I'm having problems getting frysby.js to accept non-JSON response documents. Whenever a response returns non-JSON content, throw a TypeError due to 'Unexpected token b in JSON at position 0'.
As an example, I'm sending a HTTP POST request with the JSON document shown below is expected to return a response with a plaintext document with the string bar.
{
"foo":{
"name":"bar"
}
}
Here's the unit test that I've wrote to verify the response:
it('should create a foo resource', function () {
return frisby.post('http://localhost:8080/',
{
"foo":{
"name":"bar"
}
})
.expect('status',201);
});
Unfortunately, the following error is thrown by frisby.js when I run the test:
FAIL ./test.js
✕ should create a foo resource (17ms)
● should create a foo resource
TypeError: Invalid json response body: 'bar' at http://localhost:8080/ reason: 'Unexpected token b in JSON at position 0'
Does anyone know if it's possible to configure each test to expect some data format other than JSON?
If you getting JSON + something then break jsonTypes in two format like one for JSON object and one for other, like it having array in JSON objects.
and then put expect conditions on them.
This might helps you:
const frisby = require('frisby');
const Joi = frisby.Joi;
frisby.globalSetup({
headers : {
"Accept": "application/json",
"content-type" : "application/json",
}
});
it("should create a foo resource", function () {
frisby.post("http://localhost:8080/")
.expect("status", 200)
.expect("header", "content-type", "application/json; charset=utf-8")
.expect("jsonTypes", "data.foo", {
"name": Joi.string()
})
.then(function(res) { // res = FrisbyResponse object
var body = res.body;
body = JSON.parse(body);
expect(body.data.foo.name).toBeDefined();
})
});
Related
I'm just starting with Fastify and ran into an issue where I started creating a get request to get an item by its title, but it keeps giving me a response of null when I try and log the request.body.
My get request:
// Get points by title
fastify.route({
method: 'GET',
url: '/pointz/title',
handler: (request, reply) => {
return request.body
}
})
My get request in postman (as code):
curl --location --request GET 'http://localhost:3000/pointz/title' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "test"
}'
Screenshot of my input and output in Postman
The expected result would be that the request returns a body that contains the JSON that's being sent, so I can change the code to access request.body.title with a return value of test.
UPDATE:
Found a workaround for now. If I change the GET request to a POST request it works just like it's supposed to. I question however if this is considered good practice.
As discussed in this GitHub Issue GET request validated scheme body?
the body should not be send in GET request, a datailed answer about it
and fastify ignore it by default.
But legacy code does, and if you need to support it, you have to consume the raw request:
const fastify = require('fastify')({ logger: true })
fastify.get('/', async (req) => {
let body = ''
for await (const data of req.raw) {
body += data.toString()
}
return body
})
fastify.listen(3000)
Then your curl command will works as expected.
const got = require('got')
got.get('http://localhost:3000/', {
json: { hello: 'world' },
allowGetBody: true
}).then(res => {
console.log('Returned = ' + res.body)
})
I am pulling my hairs off. On http request, i can print a body object but can not access its content.
So i am sending a request like this from web:
return fetch('https:xxxxxxx/xxxx', {
method: 'post',
body: JSON.stringify(saleObject),
headers: {
'Accept': 'application/json',
'contentType':"application/json",
'dataType':"json",
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID;
});
}
On my server (nodejs express Firebase ) , I am trying to read it in multiple ways :
exports.payPalIntent = functions.https.onRequest(async(req, res) => {
return cors(req, res, async () => {
console.log("req.body",req.body); //this print AN OBJECT, A REAL OBJECT NOT A STRING
console.log("req address",req.body.address); //=undefined, there is address property inside which
return res.send(200);
});
So, the first one print this :
req.body {"address":{"city":"some city","zip":"345334","area":"USA","street":"Hai 13", ........
The second says Cannot read property 'address' of undefined
Of course, i tried also JSON.stringify(req.body) , which print a string object that i can't access.
How do you access this object ?
JSON.stringify turns a JavaScript object into a JSON string. You already have a JSON string and you want to covert it to a JavaScript object, so use the reverse: JSON.parse(req.body). Then you should be able to access the properties using the dot operator.
I have to implement the following workflow:
Make request no.1, get a JSON response. Manipulate this JSON object so that it can be used as payload in the second request. Make second request.
The first request is successfully implemented. I get the correct JSON object. The manipulation part is also done and tested.
I managed to create a correct postman collection for the second request. It uses the method POST, has several headers, Content-type is multipart/form-data and the payload from request no.1 is sent as Raw (in the body, options none, form-data etc ... I choose raw).
I am trying to implement this second request but get an error no matter what I try to tweak. The following is my code:
const manipulatedObject = await this._flattenPayload(payload);
const Request = require(request-promise);
const options = {
method: 'POST',
uri: 'https://second_request_url',
formData: {
file: {
value: manipulatedObject,
},
},
headers: {
Accept: 'application/json, text/plain, */*',
Connection: 'keep-alive',
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryxxxxxxxxxxxxxxxx', // this is generated dynamically and matches the value in the manipulatedObject
cookie: this.cachedCookie,
'X-XSRF-TOKEN': this.cachedToken,
},
};
Request(options)
.then((body) => {
return body;
})
.catch((error) => {
return error;
});
* The parameter payload is the response from the first request.
The error I get is this:
message=source.on is not a function, stack=TypeError: source.on is not
a function
The variable manipulatedObject is a string. Currently I am copying it's value from the successful postman request to avoid errors from the manipulation process. The random token in the Content-Type header matches the ones in the manipulatedObject string. So the data are identical to the ones I use in the postman request.
I suspect the error is in the way I send the data, but I am having trouble figuring it out. I have read about this error and it seems to be generated when an object is passed to formData, but I am sending a string so I don't understand why it happens.
The values of formData accepts only three types of elements viz. string, Buffer and Stream. Refer to:request/issues/2366
U may change formData as follows:
formData: {
file: manipulatedObject,
},
or
formData: {
file: {
value: manipulatedObject,
options: {},
},
},
I am using Node.js 8.10 in lambda proxy integration.
My goal is very simple.
send json data to lambda function
query rds with json data and retrive data from rds
return response with json data in lambda from rds data.
but I faced issue in step 1. I tried to figure out and I asked aws support center. it was not helpful. please help me.
my test json data are :
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
my code is very simple but have a problem :
exports.handler = async (event) => {
var body = JSON.parse(event)//<--this is a problem
let responseBody = {
message: "hello",
key1: body.key1
};
const response = {
statusCode: 200,
headers : {"Access-Control-Allow-Origin" : "*"},
body: JSON.stringify(responseBody)
};
return response;
};
I got this error in second line.
{"errorMessage":"Unexpected token o in JSON at position 1","errorType":"SyntaxError","stackTrace":["JSON.parse (<anonymous>)","exports.handler (/var/task/index.js:2:21)"]}
So I changed second line code like this.
var body = JSON.parse(JSON.stringify(event))
and then I got response well. I thought that's working well.
{
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*"
},
"body": "{\"message\":\"hello\",\"key1\":\"value1\"}"
}
but I sent same json data with postman for real. but I got only this.
{
"message": "hello"
}
there isn't "key1". so I added console.log and then I checked log in cloudwatch.
{ message: 'hello', key1: undefined }
I can see aws editor log is well but when I send data with postman. lambda couldn't parse my json data. it means lambda couldn't parse event parameter.
my questions are :
1. when I tried to parse like this
var body = JSON.parse(event)"
why does lambda editor issue error? Is there something wrong? this is super simple and very common code.
How can I parse my json data and return correct value not undefined. I expected this response
{ message: 'hello', key1: 'value1' }
I have been trying to solve this for 3days. but I really have no idea. please help me out.
The body coming from API Gateway is a stringified JSON, so you need to parse it and then access the attribute you want.
'use strict';
exports.handler = async (event) => {
let responseBody = {
message: "hello",
key1: JSON.parse(event.body).key1
};
const response = {
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
body: JSON.stringify(responseBody)
};
return response;
};
This will do it.
you need to pass the parameters in postman as JSON.
in the body tab choose the 'raw' option and then from the combo box select 'JSON' type:
then just type :
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
and in your lambda function access to it like this:
JSON.parse(event.body).key1
I am trying to send the content-type in my superagent post request to multipart/form-data.
var myagent = superagent.agent();
myagent
.post('http://localhost/endpoint')
.set('api_key', apikey)
.set('Content-Type', 'multipart/form-data')
.send(fields)
.end(function(error, response){
if(error) {
console.log("Error: " + error);
}
});
The error I get is:
TypeError: Argument must be a string
If I remove the:
.set('Content-Type', 'multipart/form-data')
I don't get any error but my back end is receiving the request as content-type: application/json
How can I force the content type to be multipart/form-data so that I can access req.files()?
First, you do not mention either of the following:
.set('Content-Type', 'multipart/form-data')
OR
.type('form')
Second, you do not use the .send, you use .field(name, value).
Examples
Let's say you wanted to send a form-data request with the following:
two text fields: name and phone
one file: photo
So your request will be something like this:
superagent
.post( 'https://example.com/api/foo.bar' )
.set('Authorization', '...')
.accept('application/json')
.field('name', 'My name')
.field('phone', 'My phone')
.attach('photo', 'path/to/photo.gif')
.then((result) => {
// process the result here
})
.catch((err) => {
throw err;
});
And, let's say you wanted to send JSON as a value of one of your fields, then you'd do this.
try {
await superagent
.post( 'https://example.com/api/dog.crow' )
.accept('application/json')
.field('data', JSON.stringify({ name: 'value' }))
}
catch ( ex ) {
// .catch() stuff
}
// .then() stuff...
Try
.type('form')
instead of
.set('Content-Type', 'multipart/form-data')
See http://visionmedia.github.io/superagent/#setting-the-content-type
It is not clear what is in the fields variable that you are sending, but here is some information that may help you determine where your problem lies.
To begin with, if you are actually trying to build a multi-part request, this is the official documentation for doing so: http://visionmedia.github.com/superagent/#multipart-requests
as for the error that you got...
The reason is that during the process of preparing the request, SuperAgent checks the data to be sent to see if it is a string. If it is not, it attempts to serialize the data based on the value of the 'Content-Type', as seen below:
exports.serialize = {
'application/x-www-form-urlencoded': qs.stringify,
'application/json': JSON.stringify
};
which is used here:
// body
if ('HEAD' != method && !req._headerSent) {
// serialize stuff
if ('string' != typeof data) {
var serialize = exports.serialize[req.getHeader('Content-Type')];
if (serialize) data = serialize(data);
}
// content-length
if (data && !req.getHeader('Content-Length')) {
this.set('Content-Length', Buffer.byteLength(data));
}
}
this means that to set a form 'Content-Type' manually you would use
.set('Content-Type', 'application/x-www-form-urlencoded')
or
.type('form') as risyasin mentioned
any other 'Content-Type' will not be serialized, and Buffer.byteLength(data) will subsequently throw the TypeError: Argument must be a string exception if the value of your fields variable is not a string.
Here is what worked for me. I had a single field form, that was uploading a file. I turned the form into a HTML5 FormData element and then did it as follows:
var frm = new FormData(document.getElementById('formId'));
var url = 'url/here';
superagent.post(url)
.attach('fieldInFormName', frm.get('fieldInFormName'))
.end( function (error, response) {
//handle response
});
Please note, I tried various ways of setting the 'Content-Type' manually in superagent, and it never worked correctly because of the multipart identifier needed in the Content-Type.