{"id":308,"date":"2020-06-16T16:43:14","date_gmt":"2020-06-16T07:43:14","guid":{"rendered":"https:\/\/blog.wsd.sh\/?p=308"},"modified":"2020-06-16T16:48:50","modified_gmt":"2020-06-16T07:48:50","slug":"nodejs-book-chapter-11","status":"publish","type":"post","link":"https:\/\/blog.wsd.sh\/?p=308","title":{"rendered":"Nodejs Book: Chapter 11"},"content":{"rendered":"<p>With our Nodejs server handling forms, we now have two methods of sending data<br \/>\nfrom a client to our server. We have both raw JSON data, and multipart form<br \/>\ndata. When we send Ajax requests from the clients, we can add a header to<br \/>\nour request to tell Nodejs how to handle the information being sent to it.<\/p>\n<pre>var ajax = new XMLHttpRequest();\najax.open(\"POST\", \"\/api\/test\", true);\najax.setRequestHeader(\"Content-Type\", \"application\/json\");\najax.send(JSON.stringify({\"msg\":\"hello, world!\"}));\n\najax.onload = function() {\n\n    console.log(ajax.responseText);\n\n}\n<\/pre>\n<p>We can then use this content-type header to create a generic module for Nodejs.<br \/>\nIf the content-header is of type &#8220;application\/json&#8221;, then we know to treat the<br \/>\nincluded data as JSON, otherwise if the type is &#8220;multipart\/form-data&#8221;, then we<br \/>\nknow to treat the incoming body data as a form. We can then make a module that<br \/>\ntakes an incoming request and a callback and returns form data and files.<\/p>\n<p>File: handle-body.js<\/p>\n<pre>\"use strict\";\n\nmodule.exports = function( req, callback ) {\n\n    if(!req || !req.headers) {\n\n        callback( \"Empty request or null header\", null, []);\n\n    } else if(req.headers[\"content-type\"].indexOf(\"application\/json\") !== -1) {\n\n        handleJSON( req, callback );\n\n    } else if(req.headers[\"content-type\"].indexOf(\"multipart\/form-data\") !== -1) {\n\n        handleForm( req, callback);\n\n    } else {\n\n        callback(\"Unknown content-type: \" + req.header[\"content-type\"], null, []);\n\n    }\n\n}\n\nfunction handleJSON( req, callback ) {\n\n    var json_str, json;\n\n    json_str = \"\";\n\n    req.on(\"data\", function(data) {\n\n        json_str += data;\n\n    });\n\n    req.on(\"end\", function() {\n\n        try {\n            json = JSON.parse(json_str);\n        } catch(err) {\n            return callback(\"Uploaded data is not JSON encoded:\\n\" + json_str, null, []);\n        }\n\n        callback(null, json, []);\n\n    });\n\n}\n\nfunction handleForm( req, callback ) {\n\n    var raw_data = [];\n    var raw_length = 0;\n\n    req.on(\"data\", function(data) {\n\n        raw_data.push(data);\n        raw_length += data.length;\n\n    });\n\n    req.on(\"end\", function() {\n\n        var boundary, i, buf, file, line;\n        var buffer = Buffer.concat(raw_data, raw_length);\n\n        var ofs = [];\n\n        let form = {};\n        let files = [];\n\n        for (i = 0; i &lt; buffer.length; i++) {\n\n            if (buffer[i] !== 10) {\n                continue;\n            }\n\n            boundary = buffer.toString(\"ascii\", 0, i - 1);\n            break;\n\n        }\n\n        i = 0;\n        while (i !== -1) {\n            ofs.push(i);\n            i = buffer.indexOf(boundary, i + 1, \"ascii\");\n        }\n\n        for (i = 0; i &lt; ofs.length - 1; i++) {\n\n            buf = buffer.slice(ofs[i], ofs[i + 1]);\n            boundary = buf.indexOf(\"\\r\\n\\r\\n\", 0, \"ascii\");\n\n            let header = buf.slice(0, boundary).toString(\"ascii\");\n            let key = header.match(\/name=\"(.*?)\"\/)[1];\n            let filename = header.match(\/filename=\"(.*?)\"\/);\n\n            file = buf.slice(boundary + 4);\n\n            if (!filename) {\n\n                form[key] = file.toString(\"utf8\");\n\n            } else {\n\n                files.push({\n                    \"name\": filename[1],\n                    \"key\": key,\n                    \"data\": file\n                });\n\n            }\n\n        }\n\n        callback(null, form, files);\n\n    });\n}\n<\/pre>\n<p>While means that using this module, we can greatly simplify our code from the<br \/>\nprevious chapter.<\/p>\n<p>File: callback.js<\/p>\n<pre>\"use strict\";\n\nconst fs = require(\"fs\");\nconst http = require(\"http\");\nconst async = require(\"async\");\nconst uniqid= require(\"uniqid\");\nconst handleFile = require(\"handle-file\");\nconst handleBody = require(\"handle-body\");\n\nconst server = http.createServer();\nserver.on(\"request\", handleRequest);\nserver.listen(8080, handleListen);\n\nfunction handleRequest(req, res) {\n\n    console.log(req.method);\n\n    if(req.method === \"GET\") {\n\n        handleFile(req, res);\n\n    } else if(req.method === \"POST\") {\n\n        handleBody(req, function( err, form, files ) {\n            if(err) {\n                res.writeHead(500, {\"Content-Type\" : \"text\/plain\"});\n                return res.end(\"Unable to handle body\");\n            }\n\n            switch(req.url) {\n                case \"\/api\/form\":\n\n                    handleFiles( res, files );\n\n                break;\n                default:\n                    res.writeHead( 204, { \"Content-Type\" : \"text\/plain\" });\n                    res.end( \"Empty Response\" );\n                break;\n            }\n        });\n\n    } else {\n\n        res.writeHead( 204, { \"Content-Type\" : \"text\/plain\" });\n        res.end( \"Empty Response\" );\n\n    }\n\n}\n\nfunction handleListen( ) {\n\n    console.log(\"Server is listening on port 8080\");\n\n}\n\nfunction handleFiles( res, files ) {\n\n    var array = [];\n\n    async.eachSeries(files, function(file, nextFile) {\n\n        array.push(\"\/img\/\" + file.name);\n        fs.writeFile(\"public\/img\/\" + file.name, file.data, function(err) {\n            if(err) {\n                throw err;\n            }\n\n            nextFile();\n        });\n\n    }, function () {\n\n        res.writeHead(200, { \"Content-Type\" : \"text\/plain\" });\n        res.end(JSON.stringify(array));\n\n    });\n\n}\n<\/pre>\n<p>Now that we are able to handle incoming data from the client, we need a way to<br \/>\nstore and manage it. The most common way to accomplish this is to use a readily<br \/>\navailable Database server. In the next chapter we will learn how to connect,<br \/>\nwrite to, and read from a MariaDB database.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this chapter we look at the Content Type from the client to be able to separate requests to the server as either requests for a file or requests for a function to be called on the server. In this case we separate out the requests and create an echo server to return the arguments back to the client.<\/p>\n","protected":false},"author":1,"featured_media":249,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[4],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/308"}],"collection":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=308"}],"version-history":[{"count":3,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/308\/revisions"}],"predecessor-version":[{"id":311,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/308\/revisions\/311"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/media\/249"}],"wp:attachment":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=308"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}