Node: Basic JSON request - json

I'm a complete beginner in Node.js and I wanted to consult something I could not figure out.
Even though I've researched extensively I could not find any method to receive JSON request without using a plugin. I will be using it to program a mobile application API. But even though I've incluede parameter request I cannot reach the content by using request.body, or request.data. The request I'm trying to make is;
{
"id":"123"
}
And my failing code is;
var http = require('http');
function onRequest(request, response){
console.log("Request: "+request.data+"\n");
response.writeHead(200, {"Content-Type":"text/plain"});
response.write("Hello, World");
response.end();
}
http.createServer(onRequest).listen(8888);

The problem here is that you're not listening to the request events to let you know that you have data, and then parsing the data. You're assuming that request has request.data.
It should be:
var http = require('http');
function onRequest(request, response){
var data = '';
request.setEncoding('utf8');
// Request received data.
request.on('data', function(chunk) {
data += chunk;
});
// The end of the request was reached. Handle the data now.
request.on('end', function() {
console.log("Request: "+data+"\n");
response.writeHead(200, {"Content-Type":"text/plain"});
response.write("Hello, World");
response.end();
});
}
http.createServer(onRequest).listen(8888);
See this for the documentation for the methods that request contains.

Related

Displaying html with images using fs

I need to display in my server an html containing some text and stuffs, which are not important here, and the first image found inside the folder given as parameter.
I found another answer where fs.readFile was used to retrieve the html file drectly, with the image already in there, but in this case I need to build an html for every image.
I've tried this, but it doesn't show any image:
var http = require('http');
const fs = require('fs');
function read_dir(req, res, path="./images") {
let image_urls = fs.readdirSync(path);
let src = path.split("").slice(1).join("");
let image = `<img id="image" src="http://localhost:8888${src}/${image_urls[0]}">`;
res.end(image);
}
function onrequest(request, response) {
// Programs what is going to be sent back to the browser.
console.log("HTTP request received");
// Parses command line arguments
var myArgs = process.argv.slice(2);
response.writeHead(200,
{"Content-Type": 'text/html'}
);
response.write(`<h1>SLIDESHOW AREA</h1>`);
read_dir(request, response, myArgs[0]);
}
// The http.createServer() method turns my computer into an HTTP server.
var server = http.createServer(onrequest);
// The server.listen() method creates a listener on the specified port or path.
// server.listen(port, hostname, backlog, callback);
server.listen(8888);
console.log("Listening on http://localhost:8888/");
Okay, so I found out what was missing. If you're one like me that understand really poorly servers, this might be helpful.
<img src="http://localhost:8888/image/jpeg/campus1.jpg">
When the server reads the image src, it sends a GET request with url /image/jpeg/campus1.jpg. Right now the server can display HTML elements, but it cannot display JPEG elements, which is fundamental here.
It's really subtle, the image has to be read from its folder, and be set up correctly before it can be displayed.
So, the correct way to do it is to make a route inside the server that reads images and tells the server that they are of type 'image/jpeg'. At that point, the images are ready and baked to be read by the HTML.
var http = require('http');
const fs = require('fs');
let routes = { "/": main, "/image": fileRouter }
function main(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write("<h1>HI</h1>");
res.write("<h2>Here's an image</h2>");
res.end(`<img src="http://localhost:8888/image/jpeg/campus1.jpg">`);
}
function fileRouter(req, res) {
let path = req.url;
console.log(path);
fs.readFile("."+path, function(err, data) {
if(err) {
res.writeHead(500, { 'Content-Type' : 'text/plain' });
res.end('500 - Internal Error');
} else {
res.writeHead(200, { 'Content-Type' : 'image/jpeg' });
res.end(data);
}
});
}
function onrequest(req, res) {
//Parse Request
let url = req.url;
let route = url.split("/")[1];
console.log(req.method, req.url);
//Route Request
if (typeof routes["/" + route] === 'function') {
routes["/" + route](req, res);
} else {
res.writeHead(404); //not found
res.end();
}
}
var server = http.createServer(onrequest);
server.listen(8888);
I wish programming wouldn't require always my head to get dislodged, things don't always make sense, I don't get sometimes why people designed things this way. But well, also love's not always easy. Have a nice day everyone!
This tag <img id="image" src="http://localhost:8888${src}/${image_urls[0]}"> are trying to access to your server a the "http://localhost:8888${src}/${image_urls[0]}" url.
So you need to serve it for the browser to be able to download it.
An jpeg image is served with the 'Content-type: image/jpeg'header, but in the case of large images you need to make a more complex work, like cache memory, streaming, things like that; or the load time of your page will drop down easily.
It is recomended to use services like cloudinary to serve this type of content.

CouchDb 2.1.1 Admin API Compaction PUT Request

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));

Sending nested object via post request

I'm running this little node express server, which is supposed to check if the voucher is valid later and then send an answer back to the client
this is my code
app.post('/voucher', function (request, response) {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Request-Method', '*');
response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
response.setHeader('Access-Control-Allow-Headers', 'authorization, content-type');
if ( request.method === 'OPTIONS' ) {
response.writeHead(200);
response.end();
return;
}
console.log(request)
let results;
let body = [];
request.on('data', function(chunk) {
body.push(chunk);
}).on('end', function() {
results = Buffer.concat(body).toString();
// results = JSON.parse(results);
console.log('#### CHECKING VOUCHER ####', results)
let success = {success: true, voucher: {name: results,
xxx: 10}}
success = qs.escape(JSON.stringify(success))
response.end(success)
} )
}
);
It is obviously just an example and the actual check is not implemented yet. So far so good.
Now on the client side where I work with REACT, I can not seem to decode the string I just send there.
there I'm doing this
var voucherchecker = $.post('http://localhost:8080/voucher', code , function(res) {
console.log(res)
let x = JSON.parse(res)
console.log(x)
console.log(qs.unescape(x))
It gives me the error
Uncaught SyntaxError: Unexpected token % in JSON at position 0
When I do it the other way arround
let x = qs.unescape(res)
console.log(x)
console.log(JSON.parse(x))
Than it tells me
Uncaught TypeError: _querystring2.default.unescape is not a function
Maybe you can help me? I don't know what the issue is here. Thank you.
Also another question on this behalf, since I'm only a beginner. Is there smarter ways to do such things than I'm doing it now? I have react which renders on the client and I have a mini express server which interacts a few times with it during the payment process.
The both run on different ports.
What would be the standard way or best practice to do such things?
I'm a bit perplexed as to why your backend code has so much going on in the request.
Since you asked for if there is a different way to write this, I will share with you how I would write it.
Server
It seems that you want your requests to enable CORS, it also seems that you originally wanted to parse a JSON in your request body.
This is how I would recommend you re-write your endpoint
POST /voucher to take a request with body JSON
{
code: "xxxxx"
}
and respond with
{
success: true,
voucher: {
name: results,
xxx: 10
}
}
I would recommend you use express's middleware feature as you will probably use CORS and parse JSON in most your requests so in your project I would.
npm install body-parser
npm install cors
then in your app initialization
var bodyParser = require('body-parser')
var cors = require('cors')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json you can choose to just pars raw text as well
app.use(bodyParser.json())
// this will set Access-Control-Allow-Origin * similar for all response headers
app.use(cors())
You can read more about body-parser and cors in their respective repos, if you don't want to use them I would still recommend you use your own middleware in order to reduse future redundancy in your code.
So far this will substitute this part of your code
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Request-Method', '*');
response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
response.setHeader('Access-Control-Allow-Headers', 'authorization, content-type');
if ( request.method === 'OPTIONS' ) {
response.writeHead(200);
response.end();
return;
}
console.log(request)
let results;
let body = [];
request.on('data', function(chunk) {
body.push(chunk);
}).on('end', function() {
results = Buffer.concat(body).toString();
// results = JSON.parse(results);
Now your route definition can just be
app.post('/voucher', function (request, response) {
var result = request.body.code // added by body-parser
console.log('#### CHECKING VOUCHER ####', result)
// express 4+ is smart enough to send this as json
response.status(200).send({
success: true,
voucher: {
name: results,
xxx: 10
}
})
})
Client
your client side can then be, assuming $ is jquery's post function
var body = {
code: code
}
$.post('http://localhost:8080/voucher', body).then(function(res) {
console.log(res)
console.log(res.data)
return res.data
})

Node Express 4 get header in middleware missing

I have a middleware function using Node's Express4 to log each request & response for debugging. I use the res.json call in the request handler to send back JSON to the client for all but static files. So I do not want to log the response for static files, but only the JSON responses. I have the following code:
function logRequests(req, res, next) {
// do logging (will show user name before authentication)
logger.reqLog('IN '+req.method+' '+req.url, req);
var oldEnd = res.end,
oldWrite = res.write,
chunks = [];
res.write = function(chunk) {
chunks.push(chunk);
oldWrite.apply(res, arguments);
};
res.end = function(chunk, encoding) {
if(chunk) {
chunks.push(chunk);
}
oldEnd.apply(res, arguments);
// the content-type prints "undefined" in some cases
// even though the browser shows it returned as "application/json"
console.log('type='+res.get('content-type'));
if(res.get('content-type') === 'application/json') {
var body = Buffer.concat(chunks).toString('utf8');
logger.info(body, req);
}
logger.reqLog('OUT '+req.method+' '+req.path, req);
};
next(); // make sure we go to the next routes and don't stop here
}
So why do some requests show the correct content type in the middleware meaning they also print the response fine and others do not? All of them look good in the REST client when inspecting the returned headers.
EDIT: Some more info discovered tonight while trying to figure this out - if I append any character as a dummy request parameter, it logs the response type correctly:
http://localhost:8081/node/ionmed/api/logout?0 WORKS
where
http://localhost:8081/node/ionmed/api/logout DOES NOT
Also, I can always get a response type logging in the middleware function if I replace the .json() call with .end() so this:
res.json({ item: 'logout', success: true });
becomes:
res.set('content-type', 'application/json');
res.end({ item: 'logout', success: true });

How to catch http client request exceptions in node.js

I've got a node.js app that I want to use to check if a particular site is up and returning the proper response code. I want to be able to catch any errors that come up as the domain name isn't resolving or the request is timing out. The problem is is that those errors cause Node to crap out. I'm new to this whole asynchronous programming methodology, so I'm not sure where to put my try/catch statements.
I have an ajax call that goes to something like /check/site1. Server side that calls a function which attempts to make a connection and then return the statusCode. It's a very simple function, and I've wrapped each line in a try/catch and it never catches anything. Here it is:
function checkSite(url){
var site = http.createClient(80, url);
var request = site.request('GET', '/', {'host': url});
request.end();
return request;
}
Even with each of those lines wrapped in a try/catch, I will still get uncaught exceptions like EHOSTUNREACH and so on. I want to be able to catch those and return that to the ajax call.
Any recommendations on what to try next?
http.createClient has been deprecated.
Here is a quick example of how to handle errors using the new http.request:
var http = require("http");
var options = {
host : "www.example.com"
};
var request = http.request(options, function(req) {
...
});
request.on('error', function(err) {
// Handle error
});
request.end();
Unfortunately, at the moment there's no way to catch these exceptions directly, since all the stuff happens asynchronously in the background.
All you can do is to catch the uncaughtException's on your own:
var http = require('http');
function checkSite(url){
var site = http.createClient(800, url);
var request = site.request('GET', '/', {'host': url});
request.end();
return request;
}
process.on('uncaughtException', function (err) {
console.log(err);
});
checkSite('http://127.0.0.1');
Which in this case (notice port 800) logs:
{ message: 'ECONNREFUSED, Connection refused',
stack: [Getter/Setter],
errno: 111,
syscall: 'connect' }
Node.js is still under heavy development and there sure will be a lot of progress in the next couple of months, right now focus seem to be on fixing performance bugs for 3.x and making the API somewhat stable, because after all Node.js is mainly a server so throughput matters.
You can file a bug though, but be warned crashes etc. have way higher priority than features, and most new features make it in via fork pull requests.
Also for the current Roadmap of Node.js watch this talk by Ryan Dahl (Node's Creator):
http://developer.yahoo.com/yui/theater/video.php?v=yuiconf2010-dahl
I stumbled across another solution while I was researching a similar problem. http.Client emits an 'error' event if a connection can't be established for any reason. If you handle this event then the exception won't be thrown:
var http = require('http');
var sys = require('sys');
function checkSite(url) {
var site = http.createClient(80, url);
site.on('error', function(err) {
sys.debug('unable to connect to ' + url);
});
var request = site.request('GET', '/', {'host': url});
request.end();
request.on('response', function(res) {
sys.debug('status code: ' + res.statusCode);
});
}
checkSite("www.google.com");
checkSite("foo.bar.blrfl.org");
Of course, the connection error and the response to the request both arrive asynchronously, meaning that simply returning the request won't work. Instead, you'd have to notify the caller of the results from within the event handlers.
Actually it's even easier that the accepted answer:
function checkSite(url){
var http = require('http');
var request = http.get(url,r=>{console.log("OK",r)}).on("error",e=>{
console.log("ERROR",e)
})
}