I am working in NodeJS with CouchDB 2.1.1.
I'm using the http.request() method to set various config settings using the CouchDB API.
Here's their API reference, yes, I've read it:
Configuration API
Here's an example of a working request to set the logging level:
const http = require('http');
var configOptions = {
host: 'localhost',
path: '/_node/couchdb#localhost/_config/',
port:5984,
header: {
'Content-Type': 'application/json'
}
};
function setLogLevel(){
configOptions.path = configOptions.path+'log/level';
configOptions.method = 'PUT';
var responseString = '';
var req = http.request(configOptions, function(res){
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
console.log("oldLogLevel: " + responseString);
});
});
var data = '\"critical\"';
req.write(data);
req.end();
}
setLogLevel();
I had to escape all the quotes and such, which was expected.
Now I'm trying to get CouchDb to accept a setting for compaction.
The problem is that I'm attempting to replicate this same request to a different setting but that setting doesn't have a simple structure, though it appears to be "just a String" as well.
The CouchDB API is yelling at me about invalid JSON formats and I've tried a boatload of escape sequences and attempts to parse the JSON in various ways to get it to behave the way I think it should.
I can use Chrome's Advanced Rest Client to send this payload, and it is successful:
Request Method: PUT
Request URL: http://localhost:5984/_node/couchdb#localhost/_config/compactions/_default
Request Body: "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"60%\"}, {from, \"23:00\"}, {to, \"04:00\"}]"
This returns a "200 OK"
When I execute the following function in my node app, I get a response of:
{"error":"bad_request","reason":"invalid UTF-8 JSON"}
function setCompaction(){
configOptions.path = configOptions.path+'compactions/_default';
configOptions.method = 'PUT';
var responseString = '';
var req = http.request(configOptions, function(res){
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
console.log("oldCompaction: " + responseString);
});
});
var data = "\"[{db_fragmentation, \"70%\"}, {view_fragmentation, \"60%\"}, {from, \"23:00\"}, {to, \"04:00\"}]\"";
req.write(data);
req.end();
}
Can someone point at what I'm missing here?
Thanks in advance.
You need to use node's JSON module to prepare the data for transport:
var data = '[{db_fragmentation, "70%"}, {view_fragmentation, "60%"}, {from, "23:00"}, {to, "04:00"}]';
// Show the formatted data for the requests' payload.
JSON.stringify(data);
> '"[{db_fragmentation, \\"70%\\"}, {view_fragmentation, \\"60%\\"}, {from, \\"23:
00\\"}, {to, \\"04:00\\"}]"'
// Format data for the payload.
req.write(JSON.stringify(data));
Related
I am using the following javascript on a webpage to send information to a Node.js server upon a "click" on an image. This is using a 'POST' request.
<script>
function rerouter(_sent) {
var _people = <%- JSON.stringify(member_list) %>;
//convert the passed ('member_list') array into a JSON string...
var _attend = <%- JSON.stringify(online) %>;
//convert the passed ('online') array into a JSON string...
var splits = _sent.id.split("_"); //"split" on "underscore ('_')"
if (_people.indexOf(splits[1]) != -1) {
//**SEND INFO TO SERVER...
var available = _attend[_people.indexOf(splits[1])];
var response = fetch("members/pages/:" + splits[1] + "/presence/:" + available, {
method: 'POST',
headers: {
'Content-Type': 'text/plain;charset=utf-8'
}
});
//**
} //'_people' array contains the member name ('splits[1]')...
}
</script>
And here I handle the request in my Node.js server code:
var bodyParser = require('body-parser')
// create application/json parser
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.post('/members/pages/:membername/presence/:online', urlencodedParser, function (req, res) {
console.log("I RECEIVED FROM CLIENT THE FOLLOWING:")
console.log(req.params)
console.log(req.body)
res.redirect('/_landing');
})
Here is my console output:
I RECEIVED FROM CLIENT THE FOLLOWING:
{ membername: ':Nica', online: ':Yes' }
{}
As can be seen from my output, the POST route does seem to be functional, somewhat. However my 'redirect' command does NOT execute...the webpage does not change to the '_landing' page as it should...I think it may be because I am using 'fetch' to send the POST request...??? Can somebody verify if that is the cause (or another issue is the cause) and how I might be able to correct the issue?
In addition why does my 'params' include the colons (":") when I log to the console...is that standard? I would not think it would include the colons in the log, only the actual data.
Basically it seems my POST is almost working...but not exactly. Any suggestions would be greatly appreciated. I thank you in advance.
UPDATE: I have made some changes and my POST seems to be working fine now. In my frontend webpage I use the following to make the HTTP POST request:
<script>
function rerouter(_sent) {
var _people = <%- JSON.stringify(member_list) %>;
//convert the passed ('member_list') array into a JSON string...
var _attend = <%- JSON.stringify(online) %>;
//convert the passed ('online') array into a JSON string...
var splits = _sent.id.split("_"); //"split" on "underscore ('_')"
if (_people.indexOf(splits[1]) != -1) {
//**SEND INFO TO SERVER...
var available = _attend[_people.indexOf(splits[1])];
fetch('/members/pages/callup', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: splits[1], presence: available, str: 'Some string: &=&'})
})
//**
} //'_people' array contains the member name ('splits[1]')...
}
</script>
...And modified my route handler in my Node.js script:
// create application/json parser
var jsonParser = bodyParser.json()
app.post('/members/pages/callup', jsonParser, function (req, res) {
console.log("I RECEIVED FROM CLIENT THE FOLLOWING:")
console.log(req.body)
res.redirect('/_landing');
})
This is functional...to receive the data sent from the frontend webpage.
The only remaining problem is why does the 'redirect' not fire...??? I still have a feeling that by using a 'fetch' that somehow this is interfering with the page redirection...? A fetch would normally be used to wait for a response from the server, in my case I am not interested in that functionality I just want to send data one-way from frontend to backend...and then redirect the frontend page. I cannot think of any other reason why the redirect does not fire...?
Make extented:true instead of false as,
var urlencodedParser = bodyParser.urlencoded({ extended: true }) and move this line above of the below statement,
var jsonParser = bodyParser.json() and check if it works.
And finally change your headers here from,
headers: {
'Content-Type': 'text/plain;charset=utf-8'
}
To,
headers: {
'Content-Type': 'application/json'
}
Hope this will resolve the issue.
I tried to get a random Wikipedia page over their API via Google Cloud Functions. The Wikipedia API works fine. This is my request:
https://de.wikipedia.org/w/api.php?action=query&format=json&generator=random
For testing you can change the format to jsonfm in see the result in the browser. Click here 👍.
But it seems that my functions get destroyed even before the request was completely successfully. If I want to parse the data (or even if I want to log that data) I got a
SyntaxError: Unexpected end of json
The log look like (for example) that (no I haven't cut it by myself):
DATA: ue||"},"query":{"pages":{"2855038":{"pageid":2855038,"ns":0,"title":"Thomas Fischer
Of course, that is not a valid json and can't be parsed. Whatever this is my function:
exports.randomWikiPage = function getRandomWikiPage (req, res) {
const httpsOptions = {
host: "de.wikipedia.org",
path: "/w/api.php?action=query&format=json&generator=random"
};
const https = require('https');
https.request(httpsOptions, function(httpsRes) {
console.log('STATUS: ' + httpsRes.statusCode)
console.log('HEADERS: ' + JSON.stringify(httpsRes.headers))
httpsRes.setEncoding('utf8')
httpsRes.on('data', function (data) {
console.log("DATA: " + data)
const wikiResponse = JSON.parse(data);
const title = wikiResponse.query.title
res.status(200).json({"title": title})
});
}).end();
};
I've already tried to return something here. Like that video explained. But as I look into the node docs https.request don't return a Promise. So return that is wrong. I've also tried to extract the on('data', callback) into it's own function so that I can return the callback. But I haven't a success with that either.
How have to look my function that it return my expected:
{"title": "A random Wikipedia Page title"}
?
I believe your json comes through as a stream in chunks. You're attempting to parse the first data chunk that comes back. Try something like:
https.request(httpsOptions, function(httpsRes) {
console.log('STATUS: ' + httpsRes.statusCode)
console.log('HEADERS: ' + JSON.stringify(httpsRes.headers))
httpsRes.setEncoding('utf8')
let wikiResponseData = '';
httpsRes.on('data', function (data) {
wikiResponseData += data;
});
httpRes.on('end', function() {
const wikiResponse = JSON.parse(wikiResponseData)
const title = wikiResponse.query.title
res.status(200).json({"title": title})
})
}).end();
};
Second question for the day :)
Still working on my first ever app, and I've hit a bit of a snag using an API that returns currency exchange values. I need to extract the current AUD value from this JSON :
{"base":"USD","date":"2016-05-30","rates":{"AUD":1.3919,"BGN":1.7558,"BRL":3.6043,"CAD":1.3039,"CHF":0.99273,"CNY":6.5817,"CZK":24.258,"DKK":6.6765,"GBP":0.68341,"HKD":7.7688,"HRK":6.7195,"HUF":281.72,"IDR":13645.0,"ILS":3.8466,"INR":67.139,"JPY":111.19,"KRW":1190.9,"MXN":18.473,"MYR":4.1175,"NOK":8.3513,"NZD":1.4924,"PHP":46.73,"PLN":3.9447,"RON":4.0428,"RUB":65.89,"SEK":8.3338,"SGD":1.3811,"THB":35.73,"TRY":2.9565,"ZAR":15.771,"EUR":0.89775}}
Here is the code I am using:
var http = require('http');
var options = {
host: 'api.fixer.io',
port: 80,
path: '/latest?base=USD',
method: 'GET'
};
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function (chunk) {
const json = JSON.parse(chunk);
rate = json.AUD;
console.log(rate);
});
}).end();
Unfortunately this doesn't work, and I assume that is because the JSON is nested? How do I go about querying this nested string correctly?
I also know I need to tighten up my handling of chunks, but it's baby steps for me right now :)
Thank you!
not json.AUD, it is
json.rates.AUD
You should wait for whole data first, or use one of the streaming parsers instead (for example: https://github.com/dominictarr/JSONStream).
That is because "chunk" is not all data at once - it may be just part of it, which means it's not a valid JSON itself.
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
var data = '';
res.setEncoding('utf8');
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
const json = JSON.parse(data);
// As #huaoguo mentioned, it should be `json.rates.AUD`, not `json.AUD`
rate = json.rates.AUD;
console.log(rate);
});
}).end();
Also, as #huaoguo mentioned, there should be json.rates.AUD instead of json.AUD.
it's my first app with Angular.js and I've searched so hardly but I can't find a solution:
I've got a search form that makes a request to a web server which retrieves a JSON object; the problem is that this request fails and the error function begins.
Here my code:
$scope.queryUser = {};
$scope.url = "http://xxxx:8080/MyApp/search.do?index=central&query=";
$scope.search = function(query) {
$scope.queryUser= angular.copy(query);
// Create the http post request
// the data holds the keywords
// The request is a JSON request.
$http.post($scope.url + $scope.queryUser.value).
success(function(data, status) {
$scope.status = status;
$scope.data = JSON.stringify(data);
$scope.result = data; // Show result from server in our <pre></pre> element
console.log(data);
})
.
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
console.log($scope.data);
});
and this is a sample response:
{"Workstation, Laptop#Workstation$$Laptop":[{"values":{"instanceid":"OI-4A35F9C31E114FBCB5B7668FC5E1FFB4","classid":"COMPUTERSYSTEM"},"index":"distributed"},{"values":{"instanceid":"OI-6A03793A658E45EE90E589D82B0D0962","classid":"COMPUTERSYSTEM"},"index":"distributed"}]}
Any suggestions?
Thanks in advance
I understood the problem: this is a problem of domain. Making the request calling the same webservlet it works...calling an external server it doesn't work.
DOH! I'm sorry for the question
The nodejs server 'gets' this JSON stream from Twitter and sends it to the client:
stream.twitter.com/1/statuses/filter.json?track=gadget
The data returned to the client is 'chunked' JSON and both JSON.parse(chunk) and eval('(' + chunk + ')') on the client side result in parsing errors.
Concatenating the chucked pieces and waiting for the 'end' event isn't a solution either
I noticed previous samples used something like this on the client side that apparently worked before:
socket.onmessage = function(chunk) {
data = eval("(" + chunk.data + ")");
alert(data.user.screen_name);
I'm using this on the client side and it results in a parsing error:
var socket = new io.Socket();
socket.on('message', function(chunk) {
var data = eval('(' + chunk + ')'); // parsing error
alert(data.screen_name):
I know that its successfully returning a JSON chunk with:
var socket = new io.Socket();
socket.on('message', function(chunk) {
alert(chunk): // shows a JSON chunk
Server:
response.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
Did something change or what else em I doing wrong?
UPDATE: The 'end' event does not fire because its streaming?
http.get({
headers: { 'content-type': 'application/json' },
host: 'stream.twitter.com',
path: '/1/statuses/filter.json?track...
}, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
});
// does not fire
res.on('end', function () {
});
...
I'm looking into the difference with http 1.0 and http 1.1 as far as sending chunked data.
Look at the section titled Parsing Responses in Twitter's documentation.
Parsing JSON responses from the Streaming API is simple every object is returned on its own line, and ends with a carriage return. Newline characters (\n) may occur in object elements (the text element of a status object, for example), but carriage returns (\r) should not.
On the server side, keep accumulating chunks until you see the carriage return "\r". Once the carriage return is found, extract the string up to the carriage return, and that gives us one tweet.
var message = ""; // variable that collects chunks
var tweetSeparator = "\r";
res.on('data', function(chunk) {
message += chunk;
var tweetSeparatorIndex = message.indexOf(tweetSeparator);
var didFindTweet = tweetSeparatorIndex != -1;
if (didFindTweet) {
var tweet = message.slice(0, tweetSeparatorIndex);
clients.forEach(function(client) {
client.send(tweet);
});
message = message.slice(tweetSeparatorIndex + 1);
}
});
The client becomes simple. Simply parse the socket message as JSON in its entirety.
socket.on('message', function(data) {
var tweet = JSON.parse(data);
});
#Anurag I'cant add comments, however instead of
if (chunk.substr("-1") == "\r")
it should be:
if ( chunk.charCodeAt(chunk.length-2) == 13 )
The carriage return isn't the last character.
I would recommend piping the response into a JSON parser. You can use this: https://github.com/dominictarr/JSONStream