With our Nodejs server handling forms, we now have two methods of sending data
from a client to our server. We have both raw JSON data, and multipart form
data. When we send Ajax requests from the clients, we can add a header to
our request to tell Nodejs how to handle the information being sent to it.
var ajax = new XMLHttpRequest();
ajax.open("POST", "/api/test", true);
ajax.setRequestHeader("Content-Type", "application/json");
ajax.send(JSON.stringify({"msg":"hello, world!"}));
ajax.onload = function() {
console.log(ajax.responseText);
}
We can then use this content-type header to create a generic module for Nodejs.
If the content-header is of type “application/json”, then we know to treat the
included data as JSON, otherwise if the type is “multipart/form-data”, then we
know to treat the incoming body data as a form. We can then make a module that
takes an incoming request and a callback and returns form data and files.
File: handle-body.js
"use strict";
module.exports = function( req, callback ) {
if(!req || !req.headers) {
callback( "Empty request or null header", null, []);
} else if(req.headers["content-type"].indexOf("application/json") !== -1) {
handleJSON( req, callback );
} else if(req.headers["content-type"].indexOf("multipart/form-data") !== -1) {
handleForm( req, callback);
} else {
callback("Unknown content-type: " + req.header["content-type"], null, []);
}
}
function handleJSON( req, callback ) {
var json_str, json;
json_str = "";
req.on("data", function(data) {
json_str += data;
});
req.on("end", function() {
try {
json = JSON.parse(json_str);
} catch(err) {
return callback("Uploaded data is not JSON encoded:\n" + json_str, null, []);
}
callback(null, json, []);
});
}
function handleForm( req, callback ) {
var raw_data = [];
var raw_length = 0;
req.on("data", function(data) {
raw_data.push(data);
raw_length += data.length;
});
req.on("end", function() {
var boundary, i, buf, file, line;
var buffer = Buffer.concat(raw_data, raw_length);
var ofs = [];
let form = {};
let files = [];
for (i = 0; i < buffer.length; i++) {
if (buffer[i] !== 10) {
continue;
}
boundary = buffer.toString("ascii", 0, i - 1);
break;
}
i = 0;
while (i !== -1) {
ofs.push(i);
i = buffer.indexOf(boundary, i + 1, "ascii");
}
for (i = 0; i < ofs.length - 1; i++) {
buf = buffer.slice(ofs[i], ofs[i + 1]);
boundary = buf.indexOf("\r\n\r\n", 0, "ascii");
let header = buf.slice(0, boundary).toString("ascii");
let key = header.match(/name="(.*?)"/)[1];
let filename = header.match(/filename="(.*?)"/);
file = buf.slice(boundary + 4);
if (!filename) {
form[key] = file.toString("utf8");
} else {
files.push({
"name": filename[1],
"key": key,
"data": file
});
}
}
callback(null, form, files);
});
}
While means that using this module, we can greatly simplify our code from the
previous chapter.
File: callback.js
"use strict";
const fs = require("fs");
const http = require("http");
const async = require("async");
const uniqid= require("uniqid");
const handleFile = require("handle-file");
const handleBody = require("handle-body");
const server = http.createServer();
server.on("request", handleRequest);
server.listen(8080, handleListen);
function handleRequest(req, res) {
console.log(req.method);
if(req.method === "GET") {
handleFile(req, res);
} else if(req.method === "POST") {
handleBody(req, function( err, form, files ) {
if(err) {
res.writeHead(500, {"Content-Type" : "text/plain"});
return res.end("Unable to handle body");
}
switch(req.url) {
case "/api/form":
handleFiles( res, files );
break;
default:
res.writeHead( 204, { "Content-Type" : "text/plain" });
res.end( "Empty Response" );
break;
}
});
} else {
res.writeHead( 204, { "Content-Type" : "text/plain" });
res.end( "Empty Response" );
}
}
function handleListen( ) {
console.log("Server is listening on port 8080");
}
function handleFiles( res, files ) {
var array = [];
async.eachSeries(files, function(file, nextFile) {
array.push("/img/" + file.name);
fs.writeFile("public/img/" + file.name, file.data, function(err) {
if(err) {
throw err;
}
nextFile();
});
}, function () {
res.writeHead(200, { "Content-Type" : "text/plain" });
res.end(JSON.stringify(array));
});
}
Now that we are able to handle incoming data from the client, we need a way to
store and manage it. The most common way to accomplish this is to use a readily
available Database server. In the next chapter we will learn how to connect,
write to, and read from a MariaDB database.