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.