Node Express JSON Body Parsing - javascript

I'm a newbie in Express so for this issue I've researched quite bit but I cannot get it right. So I need to pass an array like this ["1","2","3","4","5"] as a payload from Frontend, and in the Express I need to accept it and do stuff with it. So far, I can send it from Frontend and receive at Express but the content of what I receive does not look right. In the Express I receive:
POST / 200 3.239 ms - 97
{ '"1","2","3","4","5"': '' }
so I cannot do anything with this. I tried to send an object called params and receive that to do something with that, that didn't work either.
Frontend headers are like this
Request URL: http://localhost:5000/
Request Method: POST
Status Code: 200 OK
Remote Address: [::1]:5000
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 36
Content-Type: application/json; charset=utf-8
Date: Mon, 13 Dec 2021 23:06:58 GMT
ETag: W/"24-sEnfXlyl7goDTpCx3bZVIGauJsc"
Keep-Alive: timeout=5
X-Powered-By: Express
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
DNT: 1
Host: localhost:5000
Origin: http://localhost:3000
Referer: http://localhost:3000/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36
Express setup that relates to this is like
var express = require("express");
...
var app = express();
...
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post("/", (req, res) => {
res.json({ requestBody: req.body });
});
So how can I send as part of the body from frontend in ReactJS an array ["1","2","3","4","5"] then accept that as array in express and do stuff with it?

What you show as the request:
POST / 200 3.239 ms - 97
{ '"1","2","3","4","5"': '' }
And, the headers you show as:
Content-Type: application/x-www-form-urlencoded
Do not match. Your data is being sent as plain text. It's not application/json or application/x-www-form-urlencoded so you don't have a body-parser installed for it so Express doesn't know what to do with it. In fact, Express doesn't even read it, it just stays in the incoming stream.
You don't show the client-side code, but the client needs to make the content-type and the encoding of the body content you send with the POST actually match. Then, you need a body-parser for that content-type on the Express side of things.
Since this is meant to be an array of data, I would suggest using JSON and sending application/json encoded data. Then, your existing express.json() middleware will read and parse it for you and you can read the data in req.body.
If you want help fixing the client-side, then show us the client-side code that is sending this.
For example, if you were sending this from the client with fetch(), here's an example right from the MDN doc for fetch():
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
});

You should NOT send an object called params. Since its POST request, you should send the array as an property of the request body. Then it will be available in the req.body property in the Express app.

Related

Upload a custom image to Spotify playlist

I'm trying to upload a custom image to my playlist on Spotify. Here's what happens: I make a request to the API to create a playlist using AJAX (this works) and then I want to upload a custom image to that newly made playlist also using AJAX. I've been following Spotify's guide on how to do that and believe that the headers, scopes, content type, etc... are all correct. Here is the code for uploading the image:
var playlistId = "newly_created_playlist_id_here";
var token = "access_token_here";
var url = c.toDataURL({ // encodes canvas image (declared as "c" in another file) to base64 jpeg format
format: 'jpeg', // file where "url" is declared is imported via the import / export method
quality: 0.8 // canvas is successfully encoded to jpeg base64
});
$.ajax({
url: "https://api.spotify.com/v1/playlists/" + playlistId +"/images",
type: 'PUT',
body: url,
headers: {
"Authorization": "Bearer " + token,
"Content-Type": "image/jpeg; charset=utf-8"
},
contentType: "image/jpeg",
error: function(err) {
console.log('Error: ' + err);
},
success: function(data) {
alert('Load was performed.');
}
});
I however receive a error 400 from Spotify as seen below:
PUT https://api.spotify.com/v1/playlists/my_playlist_id/images
400
Here is the HTTP header file contents:
Request URL: https://api.spotify.com/v1/playlists/my_playlist_id/images
Request Method: PUT
Status Code: 400
Remote Address: 35.186.224.25:443
Referrer Policy: no-referrer-when-downgrade
access-control-allow-credentials: true
access-control-allow-headers: Accept, App-Platform, Authorization, Content-Type, Origin, Retry-After, Spotify-App-Version, X-Cloud-Trace-Context
access-control-allow-methods: GET, POST, OPTIONS, PUT, DELETE, PATCH
access-control-allow-origin: *
access-control-max-age: 604800
alt-svc: clear
cache-control: private, max-age=0
content-encoding: gzip
content-length: 86
content-type: application/json
date: Mon, 15 Jun 2020 09:53:52 GMT
server: envoy
status: 400
strict-transport-security: max-age=31536000
via: HTTP/2 edgeproxy, 1.1 google
x-content-type-options: nosniff
x-robots-tag: noindex, nofollow
:authority: api.spotify.com
:method: PUT
:path: /v1/playlists/my_playlist_id/images
:scheme: https
accept: application/json, text/javascript, */*; q=0.01
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
authorization: Bearer my_access_token
content-length: 0
content-type: image/jpeg
origin: http://127.0.0.1:5500
referer: http://127.0.0.1:5500/index.html
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36
Just to re-iterate what I've said earlier for clarification, The playlist is created with no errors and the image is encoded to base64 jpeg successfully too however when I try to upload the image to the new playlist, I receive the error 400. A new access token is also generated successfully.
I am using the following scopes when requesting access from the user:
ugc-image-upload
playlist-modify-public
playlist-modify-private
which are needed as described in the Spotify documentation.
I have no idea what I'm doing wrong here and I haven't found any other questions across the internet that answer my problem so any help is greatly appreciated!
This caused me a similar headache - it does work but the encoding of the image must be exact:
No newlines or spaces
Nothing like data:image/jpeg at the start of the string (see this answer)
While not relevant to the OP, the main issue in my case was my editor (VScode) adding a new line to the end of the text file with the encoded image when it was saved, which caused it to be rejected by the Spotify API.
For anyone else looking at this, I got it working by starting with a very basic base64 encoded image (see below) and then building up to the functionality I needed.
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
(A red dot, taken from this question.)

Javascript Fetch API Cors : Doesen't pass access control check

I am dealing with an external api. I want to post some data so i set a token in the headers to be able to access the api.
I am told that my test origin has been whitelisted http://127.0.0.1:8081/
However i get the following error.
Failed to load
https://external-api.com/api/transactions/ad2d7a69-f723-4798-9fa5-a95a76d65324/document:
Response to preflight request doesn't pass access control check: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'.
async submitDocument(transationId, token, base64) {
const url = host + "/api/transactions/" + transationId + "/document"
const body = {
"image": base64,
}
let headers = new Headers();
headers.set('Content-type', 'application/json');
headers.set('token', token);
const request = {
method: 'POST',
body: JSON.stringify(body),
mode: 'cors',
headers: headers,
credentials: 'include'
}
const data = await fetch(url, request);
const response = await data.json();
return response;
}
This function call is being made browser side on the following page. http://127.0.0.1:8081/
Response from server
Request URL: https://externalapi.com/api/transactions/f400aaec-3fde-4458-a36e-fe03d550fc00/document
Request Method: OPTIONS
Status Code: 200
Remote Address: 54.194.37.150:443
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Headers: content-type, token
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 0
Connection: keep-alive
Content-Length: 0
Date: Fri, 22 Jun 2018 15:29:27 GMT
Server: nginx
Vary: Origin
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Access-Control-Request-Headers: content-type,token
Access-Control-Request-Method: POST
Cache-Control: no-cache
Connection: keep-alive
Host: externalapi.com
Origin: http://127.0.0.1:8081
Pragma: no-cache
Referer: http://127.0.0.1:8081/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
just read the error message carefully! Your domain must not be whitelistet with '*'.
It has to be 'http://127.0.0.1:8081'. You have to ask external-api.com to recheck it.
In your request you have credentials set in a token header and the Origin of your request is:
Origin: http://127.0.0.1:8081
The request in this case will proceed only if the server answers with:
Access-Control-Allow-Origin: http://127.0.0.1:8081
Otherwise the request is blocked by the browser
Check here for more details:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Examples_of_access_control_scenarios
In particular the section "Requests with credentials"

Cant get request payload in express js node

This is my Node Express Code,
(function () {
'use strict';
var fs = require('fs');
var cors = require('cors');
var bodyParser = require('body-parser');
var express = require('express'),
app = express(),
port = 8112;
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.listen(port);
app.route('/abc')
.post(abc);
function abc(req,res){
console.dir(req.body);
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.sendStatus(200);
}
})();
But Im getting request body as
{}
But in my network Tab in Chrome I can see request payload.
Please note OPTIONS is fired before this POST call.
Request headers
POST /abcHTTP/1.1 Host: localhost:8112 Connection:
keep-alive Content-Length: 11 Pragma: no-cache Cache-Control: no-cache
Origin: http://localhost:4200 User-Agent: Mozilla/5.0 (Windows NT
10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
x-api-key:CExkxDlFC35ckfCGX6m61x76GxIYH2h2Iv8bX874
Content-Type:text/plain;charset=UTF-8
Accept: / Referer:
http://localhost:4200/dashboard
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Request Payload
{"dd":"dd"}
You need to send: Content-Type: application/json for bodyParser.json() to work, without it, your JSON payload won't be parsed, that's why you get: {}
From the docs:
The bodyParser object exposes various factories to create middlewares.
All middlewares will populate the req.body property with the parsed
body when the Content-Type request header matches the type option, or
an empty object ({}) if there was no body to parse, the Content-Type
was not matched, or an error occurred.
Example using .fetch:
fetch('http://localhost:4200/dashboard', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({dd: 'dd'})
});
Your content-type header is text/plain. Please try replacing
app.use(bodyParser.json());
with
app.use(bodyParser.text());
This normally happens because of the "Content-type" header in your http request.
JSON Bodies
bodyParser.json() only accepts the type of "application/json" and rejects other non-matching content types.
Solutions 1: Accepting Extra Content Types Other Than application/json
check your request header "Content-type" and add it to options argument in bodyParser.json([options]) in server.
for example if you have this:
"Content-type: application/csp-report"
app.use(bodyParser.json({ type: ["application/json", "application/csp-report"] }));
you can also use the built-in middleware in exprss v4.16.0 onwards:
app.use(express.json({ type: ['application/json', 'application/csp-report'] }));
for more information see this documentation
Notice: use this approach only where you can not change Content-type to application/json.
Solutions 2 ( recommended ): Change Header content-type To application/json In Http Request.
What about urlencoded bodies ?
This is similar to json bodies with two differences:
"Content-type" header accepted in request is "application/x-www-form-urlencoded"
body payload is a url encoded format (Not a json object):
Format: param_1=value_1&param_2=value_2&...
app.use(express.urlencoded({ extended: false }));
see docs.
Hi #Pradhaban Nandhakumar, I think you have to pass data as row data.
Try below code snippets.
You should always pass raw data .
app.post('/users', (req, res) => {
res.send(req.body);
});
You are posting a plain-text string instead of a JSON. Consider showing us how you are posting data (jQuery, Fetch API, etc).
Just have to specify
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
in your request headers (front-end). The Express body parser should then be able to detect the valid JSON and print the result. Note that Accept isn't required but it instructs the server that the client understands and accepts a JSON response.
Alternatively, use a simple library like Easy Fetch on the front-end to manage server-side calls without having to deal with response parsing or header settings.

Debugging MobileServiceClient request

I'm developing an Azure MobileService / CordovaApp setup. The server runs locally and has the following setting to enable CORS:
<add name="Access-Control-Allow-Origin" value="*" />
The api can be called via browser using addresses like
http://localhost:59477/api/item/explore/A/B
The client script that's trying to depict this call is the following:
var client = new WindowsAzure.MobileServiceClient('http://localhost:59477/', 'http://localhost:59477/', '');
GetItems.addEventListener("click",
function ()
{
client.invokeApi("item/explore/A/B",
{
method: "get"
})
.done(
function (results)
{
alert(results.result.count);
},
function (error)
{
var xhr = error.request;
alert('Error - status code: ' + xhr.status + '; body: ' + xhr.responseText);
alert(error.message);
}
);
}
);
What I get is status 405 - Method not allowed, which I don't understand for two reasons:
The invoked Service Action is decorated with the [HttpGet] Attribute
The response headers seem to say GET is fine:
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Length: 76
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RjpcQ29kZVxGb290UHJpbnRzXGZvb3RwcmludHMuU2VydmVyXEZvb3RwcmludHNTZXJ2aWNlXGFwaVxmb290cHJpbnRcZXhwbG9yZVw1MS4yNzcwMjJcNy4wNDA3ODM=?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Date: Sat, 15 Aug 2015 18:08:30 GMT
Any idea what I can do to get this running?
Edit
The Raw request shows that, as already expected by Jeremy Foster, the client sends a request of the type OPTIONS:
OPTIONS http://localhost:59477/api/fp/get?id=1 HTTP/1.1
Host: localhost:59477
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:4400
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36
Access-Control-Request-Headers: accept, x-zumo-features, x-zumo-installation-id, x-zumo-version
Accept: */*
Referer: http://localhost:4400/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
I managed to support this request applying the [HttpOptions] attribute to my action method. Now I get a more or less valid response:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RjpcQ29kZVxGb290UHJpbnRzXGZvb3RwcmludHMuU2VydmVyXEZvb3RwcmludHNTZXJ2aWNlXGFwaVxmcFxnZXRcMQ==?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Date: Sun, 23 Aug 2015 19:34:21 GMT
Content-Length: 234
...
At least that's what fiddler tells me. The AzureMobileServiceClient throws yet another issue, which roughly translates as:
Cross-Origin request blocked, missing token 'x-zumo-installation-id' in CORS header row 'Access-Control-Allow-Headers' on CORS-Preflight-Channel
Turn on Fiddler and try your api call from the browser and then again using the Mobile Services client and compare your raw HTTP requests to see what's different.
Try to run through the CORS Support section of this blog post: http://azure.microsoft.com/blog/2014/07/28/azure-mobile-services-net-updates/.
A side note: The x-zumo-installation-id error you were getting was because the client requested access for a list of headers (see the Access-Control-Request-Headers in your request) and the server did not return that same list in the Access-Control-Allow-Headers header. Using the MS_CrossDomainOrigins setting as mentioned in the above blog post takes care of this by setting the allowed headers to * (all) by default.

ngResource PUT returns bad request due to $promise and $resolved in the JSON object

I've been struggling with this problem for the last few hours, and every tutorial points toward the solution that I have implemented but it doesn't work.
Basically my PUT request returns an error:
PUT http://localhost:8083/stockapi/rest/stocks/5485cba248673a0dd82bb86f 400 (Bad Request)
When I intercept the request, I see that it contains a $promise and $resolved data element:
> {"id":"5485cba248673a0dd82bb86f","name":"iShares ESTOCK DivXXX","ticker":"AMS:IDVY","url":"https://www.google.com/finance?q=AMS%3AIDVY&ei=F5BxVLiCB8GlwQPJ1YD4DQ","currency":"EUR","currentPrice":19.81,"currentPriceInEuro":19.81,"lastModified":1418054562234,"historyStockPrices":[{"timestamp":1418054562234,"price":19.81}],"$promise":{},"$resolved":true}
This makes sense since I'm using the ngResource object -- but every tutorial shows that the following code should be able to handle it, but it doesn't.
Note/edit: if i PUT the JSON object without the "$promise" and "$resolved" elements through an external program (such as Postman REST client) then it works fine.
Factory:
.factory('Stock',function($resource){
return $resource('http://localhost:8083/stockapi/rest/stocks/:id',
{ id: '#id' },{
update: { method: 'PUT' },
show: { method: 'GET' }
}); });
Controller (note: doing 4 updates but none of them work, 4 times the same Bad Request):
.controller('StockEditController',function($scope,$log,$http,$state,$stateParams,Stock){
$scope.stock = Stock.get({id:$stateParams.id});
$scope.updateStock=function(stock) {
Stock.update(stock);
stock.$update();
Stock.update($scope.stock);
$scope.stock.$update();
$state.go('stocks');
};
});
I'm really clueless right now how to use the ngResource object in the correct way so that I can use it to put/post to my webservice. Any ideas?
Thanks!
EDIT:
Chrome network output:
Response header
Remote Address:[::1]:8080
Request URL:http://localhost:8080/stockapi/rest/stocks/5485cba248673a0dd82bb86f
Request Method:PUT
Status Code:400 Bad Request
Request Headersview parsed
PUT /stockapi/rest/stocks/5485cba248673a0dd82bb86f HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 355
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost:8080/stockapi/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Request Payloadview parsed
{"id":"5485cba248673a0dd82bb86f","name":"iShares ESTOCK DivXXXYYY","ticker":"AMS:IDVY","url":"https://www.google.com/finance?q=AMS%3AIDVY&ei=F5BxVLiCB8GlwQPJ1YD4DQ","currency":"EUR","currentPrice":19.81,"currentPriceInEuro":19.81,"lastModified":1418054562234,"historyStockPrices":[{"timestamp":1418054562234,"price":19.81}],"$promise":{},"$resolved":true}
Response Headersview parsed
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 968
Date: Tue, 09 Dec 2014 06:36:24 GMT
Connection: close
According to the docs you are not quite using the update action correctly.
So really your updateStock method should be:
$scope.updateStock=function(stock) {
Stock.update({id: stock.id}, stock);
Stock.update({id: $scope.stock.id}, $scope.stock); //not sure why you have 2 calls here
$state.go('stocks');
};
Just looking at the factory definition and how it differs from mine, you should change that second parameter of factory definition into array to something like this:
.factory('Stock',[$resource, function($resource){
return $resource('http://localhost:8083/stockapi/rest/stocks/:id',
{ id: '#id' },{
update: { method: 'PUT' },
show: { method: 'GET' }
});
}]);
Not sure if this is the issue but that is at least significantly different to angular docs' definition on dependency injection here: https://docs.angularjs.org/guide/di
If not, could you also print out the contents of $scope.stock after the GET returns the data there?
I know this question is super old, but I stumbled upon the same issue and found this SO question as well as this question:
http://www.scriptscoop2.com/t/fddc3f0a1f6f/angularjs-using-ngresource-for-crud-adds-extra-key-value-when-using-save.html
There was 1 answer which explained that the issue is coming due to CORS (cross origins). Which means that your server side needs to allow it. If you are using Spring MVC it would be enough to add the org.springframework.web.bind.annotation.CrossOrigin annotation at the controllers request mapping:
#CrossOrigin
#RequestMapping(value = "/product/{id}", method = RequestMethod.POST)
#ResponseBody
public void editProduct(#PathVariable("id") final long id, #RequestBody final ProductDto productDto) {
// your code
}

Categories