The body of the response from Authorize.net's sandbox API is:
{
"messages": {
"resultCode": "Error",
"message": [
{
"code": "E00012",
"text": "You have submitted a duplicate of Subscription 5777085. A duplicate subscription will not be created."
}
]
}
}
but when I go to parse it:
try {
bodyObj = JSON.parse(body);
} catch (ex) {
console.error(ex);
}
I get this error:
SyntaxError: Unexpected token in JSON at position 0
And this: console.log(response.headers['content-type']);
returns this: application/json; charset=utf-8
What am I doing wrong? I want to parse the JSON into a JS object.
Actually you didn't see it, but there was a invisible unicode character, specifically the byte order mark at the beginning of the JSON.
Since the byte order mark is not a valid JSON character, JSON.parse rejected it.
To remove, use the following code.
function removeByteOrderMark(str){
return str.replace(/^\ufeff/g,"")
}
// OR (faster),
let removeByteOrderMark = a=>a[0]=="\ufeff"?a.slice(1):a
We had the same issue with JSON.parse() in a project we're working on. We're just using JSON.stringfy() and the object was working fine, but on the other platform where we received the data it had a similar error "Unexpected token in JSON at position 1".
Here's how we made it work:
In NodeJs we encoded the stringified object using encodeURI() then sent it
On the client side: decoded the string then used JSON.parse() on the decoded string
Analysis:
I tried to print the characters from the position, then replaced it with an empty string, then we realized it prints other weird characters even after replacing them. After that I realized it's an HTML code """ so instead of replacing it we went for encoding the string it and decoding.
We tried it on our case and it's working no problems
Related
I tried to send the below message
Invalid query parameters detected:
=== Error No. 1 ==========
Required property is missing
● Property / element: 'Deserialized query parameters of http://127.0.0.1:90/api/categories route.paginationPageNumber'
This property is 'undefined' while has been marked as required.
● Property / element specification:
{
"type": "number",
"required": true,
"numbersSet": "NATURAL_NUMBER"
}
● Actual value: undefined
● Value before first pre-validation modification: undefined
< etc. >
via
response. // (is "http.ServerResponse")
writeHead(HTTP_StatusCodes.badRequest, errorMessage).
end();
in errorMessage variable . Below NodeJS error occured:
Caught error:
TypeError [ERR_INVALID_CHAR]: Invalid character in statusMessage
at new NodeError (node:internal/errors:371:5)
at ServerResponse.writeHead (node:_http_server:322:11)
// ...
I have no idea on which character NodeJS complains about (possibly slashes), but I suppose what I am using the second parameter of writeHead improperly. If so, there are two questions:
Which errorMessage must be like? Just "Bad request" or "Unauthorized"?
Where I should append above login message instead?
You can refer to this for official documentation
You can send a response like this
let body = error_message; // or whatever response you want to send back to the sender
response
.writeHead(STATUS_CODE, {
'Content-Type': 'your_header_content_type'
}).end(body);
As for the status codes, you can refer the mozilla convention here (which is quite widely used)
Please guide me in case I'm not in proper use of axios. This simple piece of code can directly run:
const axios = require('axios')
axios.post('https://exp.host/--/api/v2/push/send', {"to":["ExponentPushToken[xxxxxxx]"],"title":"test title","body":"test body."})
.then(responseExpo => {
console.log("expo replied normally: " + JSON.stringify(responseExpo));
})
.catch(error => {
console.log("expo replied with error: " + JSON.stringify(error,null,4));
});
The result is:
Promise { <pending> }
expo replied with error: {}
"axios": "^0.19.2"
I tried to post with api tools and see a response with normal 200 status code:
{
"data":[
{
"status": "error",
"message": "\"ExponentPushToken[xxxxxxx]\" is not a registered push notification recipient",
"details":{
"error": "DeviceNotRegistered"
}
}
]
}
(you may ignore the "error": "DeviceNotRegistered" inside this json cos it's expected because I have put an invalid xxxxx input value when calling the api. Even putting a valid input value the result is still returning to the catch block with empty error)
I'm expecting it to return to the then block cos the server actually response with 200 with well formatted json result.
Have I done something wrong so that the call returns to the catch block? Cos the error is empty I have no idea what went wrong.
===============================
after jfriend's reminder I changed to directly disply the error.
console.log("expo replied with error: " + error);
it is show like this now:
Promise { <pending> }
expo replied with error: TypeError: Converting circular structure to JSON
--> starting at object with constructor 'ClientRequest'
| property 'socket' -> object with constructor 'TLSSocket'
--- property '_httpMessage' closes the circle
Anyone can let me know what exactly it means and guide me how to correct my usage?
(problem resolved). the response (responseExpo in the question) is neither a plain data JSON nor a plain string. it is an object with (see github.com/axios/axios#response-schema) some attributes. The real response content is inside "response.data". I was wrongly treating the response to be a plain json object or the http response content.
I had a similar problem and as solution, I used HttpService from nestjs which returns Observable<AxiosResponse<T>>. I fixed the problem by piping and plucking the request like this:
http.put<T>(url, data, config).pipe(pluck('data'))
I had a similar problem with HttpService from nestjs which returns Observable<AxiosResponse<any>>. I resolve with:
this.httpService.post(this.legacyAccessTokenEndpoint, form, { headers: form.getHeaders() }).pipe(map(x => x?.data))
I have built an API with Node and Express which returns some JSON. The JSON data is to be read by a web application. Sadly this application only accepts ISO-8859-1 encoded JSON which has proven to be a bit difficult.
I can't manage to return the JSON with the right encoding even though I've tried the methods in the Express documentation and also all tips from googling the issue.
The Express documentation says to use "res.set()" or "res.type()" but none of these is working for me. The commented lines are all the variants that I've tried (using Mongoose):
MyModel.find()
.sort([['name', 'ascending']])
.exec((err, result) => {
if (err) { return next(err) }
// res.set('Content-Type', 'application/json; charset=iso-8859-1')
// res.set('Content-Type', 'application/json; charset=ansi')
// res.set('Content-Type', 'application/json; charset=windows-1252')
// res.type('application/json; charset=iso-8859-1')
// res.type('application/json; charset=ansi')
// res.type('application/json; charset=windows-1252')
// res.send(result)
res.json(result)
})
None of these have any effect on the response, it always turns into "Content-Type: application/json; charset=utf-8".
Since JSON should(?) be encoded in utf-8, is it event possible to use any other encoding with Express?
If you look at the lib/response.js file in the Express source code (in your node_modules folder or at https://github.com/expressjs/express/blob/master/lib/response.js) you'll see that res.json takes your result, generates the corresponding JSON representation in a JavaScript String, and then passes that string to res.send.
The cause of your problem is that when res.send (in that same source file) is given a String argument, it encodes the string as UTF8 and also forces the charset of the response to utf-8.
You can get around this by not using res.json. Instead build the encoded response yourself. First use your existing code to set up the Content-Type header:
res.set('Content-Type', 'application/json; charset=iso-8859-1')
After that, manually generate the JSON string:
jsonString = JSON.stringify(result);
then encode that string as ISO-8859-1 into a Buffer:
jsonBuffer = Buffer.from(jsonString, 'latin1');
Finally, pass that buffer to res.send:
res.send(jsonBuffer)
Because res.send is no longer being called with a String argument, it should skip the step where it forces charset=utf-8 and should send the response with the charset value that you specified.
I am currently using Swagger (2.0) on Node.js. My YAML file validates correctly and the requests are going through. My particular call has a request body using the following format -
{
"postalCode": "invalidpostalcode",
"phone": "invalidnumber",
"website": "invalidwebsite"
}
I would like to validate each body variable before continuing to the next piece of code. To accomplish validating the data I would like to use validator.js.
Now my basic validation code looks something like that -
if(!validator.isURL(req.body.website)){
var tmp = {
"message": req.__('error.url', {
field: req.body.website
})
}
res.status(400).json(tmp);
return;
}
if(!validator.isPostalCode(req.body.postalCode,'any')){
var tmp = {
"message": req.__('error.postalCode', {
field: req.body.postalCode
})
}
res.status(400).json(tmp);
return;
}
Is there there any better way to validate data sent in through the API that would require less code? For example by using a function to control the majority of the logic?
while this is one example of the endpoint I will have some in the future that will require upwards of 20 body parameters.
I have tried create a function that will return the response code. However, that returns an error Cannot set headers after they are sent to the client.
Let's say I am creating a REST API with Node/Express and data is exchanged between the client and server via JSON.
A user is filling out a registration form and one of the fields is an image input to upload a profile image. Images cannot be sent through JSON and therefore must be converted to a base64 string.
How do I validate this is indeed a base64 string of an image on the serverside? Or is it best practice not to send the profile image as a base64?
You could start by checking if the string is a base64 image, with a proper mime type.
I found this library on npm registry doing exactly that (not tested).
const isBase64 = require('is-base64');
let base64str_img = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAoHBwgHBgoIC...ljA5GC68sN8AoXT/AF7fw7//2Q==';
console.log(isBase64(base64str_img, { mime: true })); // true
Then you can verify if the mime type is allowed within your app, or make other verifications like trying to display the image file and catch possible error.
Anyway, If you want to be really sure about user input, you have to handle it by yourself in the first place. That is the best practice you should care about.
The Base64 value it is a valid image only if its decoded data has the correct MIME type, and the width and height are greater than zero. A handy way to check it all, is to install the jimp package and use it as follows:
var b64 = 'R0lGODdhAQADAPABAP////8AACwAAAAAAQADAAACAgxQADs=',
buf = Buffer.from(b64, 'base64');
require('jimp').read(buf).then(function (img) {
if (img.bitmap.width > 0 && img.bitmap.height > 0) {
console.log('Valid image');
} else {
console.log('Invalid image');
}
}).catch (function (err) {
console.log(err);
});
I wanted to do something similar, and ended up Googling it and found nothing, so I made my own base64 validator:
function isBase64(text) {
let utf8 = Buffer.from(text).toString("utf8");
return !(/[^\x00-\x7f]/.test(utf8));
}
This isn't great, because I used it for a different purpose but you may be able to build on it, here is an example using atob to prevent invalid base64 chars (they are ignored otherwise):
function isBase64(text) {
try {
let utf8 = atob(text);
return !(/[^\x00-\x7f]/.test(utf8));
} catch (_) {
return false;
}
}
Now, about how it works:
Buffer.from(text, "base64") removes all invalid base64 chars from the string, then converts the string to a buffer, toString("utf8"), converts the buffer to a string. atob does something similar, but instead of removing the invalid chars, it will throw an error when it encounters one (hence the try...catch).
!(/[^\x00-\x7f]/.test(utf8)) will return true if all the chars from the decoded string belong in the ASCII charset, otherwise it will return false. This can be altered to use a smaller charset, for example, [^\x30-\x39\x41-\x5a\x61-\x7a] will only return true if all the characters are alphanumeric.