构建基于Node.js的web应用
来源:互联网 发布:php 直播功能如何实现 编辑:程序博客网 时间:2024/06/04 19:03
构建基于Node.js的web应用
(参考书籍《node入门》)
在写Node.js应用的时候我们不仅要知道如何写代码,还要知道如何组织这些代码。
下面我们来看一个简单的基于Node.js的web应用,以此来学习如何组织我们项目的代码,这个应用的主要功能是上传图片,并在浏览器中展示出来。
应用的结构
我们来看看整个应用结构:
project
+----node_modules
|----router.js
|----server.js
|----requestHandles.js
|----index.js
node_modules中存放的是项目相关的依赖。
router.js为路由,以便我们队不同的url进行不同的处理。
requestHandles.js是对不同url进行的具体的处理。
server.js为服务器模块,我们将在这里创建服务器。
index.js为整个项目的入口文件。
这个web应用,它的代码并不是简单的凑到一块,而是有一定的结构,是模块化的组织方式,不同的功能对应不同的模块(文件),这有利于代码的维护和重构,在大项目中也有利于团队的合作。
现在我们来具体到每个文件,一点一点的分析这个web应用。
1. 先来看一下server.js文件:
var http = require("http");var url = require("url")function start (route, handle) { function onRequest (request, response) { var pathname = url.parse(request.url).pathname; console.log("Requset for " + pathname + " received"); route(handle, pathname, response, request); } http.createServer(onRequest).listen(8888); console.log("Server has started.");}exports.start = start;
在这里,我们运用node.js自带的http模块的createServer()方法创建一个服务器,并在8888端口进行监听,我们可以通过访问http://localhost:8888/,在浏览器中访问该服务器。通过url模块对浏览器发来的请求request进行解析,提取出想要的信息。
另外我们可以看到,start函数接受一个route函数作为参赛,这个route便是路由,通过调用route(handle, pathname, response, request)去处理相应的请求。
2.我们再来看看router.js文件:
function route (handle, pathname, response, request) { console.log("About to route a request for" + pathname); if(typeof handle[pathname] === 'function') { handle[pathname](response, request); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/plain"}); response.write("404 Not found"); response.end(); }}exports.route = route;
在router.js文件中我们看到,route函数中接收了handle, pathname, response, request四个参数,
通过不同的pathname调用不同的handle对象中的函数,并把response和request传入handle对象中相应的函数。如果找不到相应的handle就返回一个404错误。我们继续顺藤摸瓜看看handle对象里有什么对应的函数。
3. requestHandlers.js
该文件中有start、upload、show这三个函数,分别对应:返回首页内容,上传文件图片,显示文件图片,这三个函数就是放在handle对象里的函数,
将它们单独提取出来放在一个文件中(requestHandlers.js)中是为了当项目变得越来越大的时候便与整理。
将它们单独提取出来放在一个文件中(requestHandlers.js)中是为了当项目变得越来越大的时候便与整理。
首先,我们来该文件看看需要引入的模块
var fs = require('fs'), formidable = require("formidable");
我们要将文件读取到我们的服务器中,需要使用一个叫fs的模块。故我们需要require('fs'),该模块的作用是帮助我们读取文件。formidable模块这是帮助我们分析上传的文件数据,即处理post数据。
函数start:function start(response, request) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" '+ 'content="text/html; charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end();}
start函数用于给页面返回一个表单,可以用于上传图片。
upload函数:
function upload(response, request) { console.log("Request handler 'upload' was called"); var form = new formidable.IncomingForm(); form.uploadDir = 'tmp'; console.log("about to parse"); form.parse(request, function(error, fields, files) { console.log("parsing done"); fs.renameSync(files.upload.path, "/tmp/test.png"); response.writeHead(200, {"Content-Type": "text/html"}); response.write("received image:<br/>") response.write("<img src='/show' />"); response.end(); })}
在uoload中,我们用到了formidable模块中的IncomingForm()方法,并把该方法赋给form变量,然后调用form.parse(),回调函数中我们可以转换请求中的表单数据,拿到所有的字段域和文本信息。在回调函数中,我们调用函数fs.renameSync(files.upload.path, "/tmp/test.png"),这里的意思的读取files.upload.path路径指向的图片,也就是你选择上传的图片,并将它保存到tmp文件内命名为test.png。 form.uploadDir = ‘tmp’的作用是解决跨磁盘传输文件的问题,如果没有设置uploadDir的话,跨域传输文件可能会报错。
函数show:function show(response, request) { console.log("Request handler 'show' was called.") fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } })}
show函数顾名思义,就是用来显示图片的,在函数中我们看到,我们用到了引进来的fs模块中的readFile方法来读取指定路径的文件,如何读入错误,则进行错误处理。
整个requestHanlders.js文件是这样的:
var fs = require('fs'), formidable = require("formidable");function start(response, request) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" '+ 'content="text/html; charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end();}function upload(response, request) { console.log("Request handler 'upload' was called"); var form = new formidable.IncomingForm(); form.uploadDir = 'tmp'; console.log("about to parse"); form.parse(request, function(error, fields, files) { console.log("parsing done"); fs.renameSync(files.upload.path, "/tmp/test.png"); response.writeHead(200, {"Content-Type": "text/html"}); response.write("received image:<br/>") response.write("<img src='/show' />"); response.end(); })}function show(response, request) { console.log("Request handler 'show' was called.") fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } })}exports.start = start;exports.upload = upload;exports.show = show;
4.index.js
最后我们来看一下入口文件index.jsvar server = require("./server");var router = require("./router");var requestHandlers = require("./requestHandlers");var handle = {}handle["/"] = requestHandlers.start;handle["/start"] = requestHandlers.start;handle["/upload"] = requestHandlers.upload;handle["/show"] = requestHandlers.show;server.start(router.route, handle);
可以看到我们为什么把index.js叫做入口文件,这里我们把其他三个文件server.js、router.js、requestHandlers.js都引了进来,然后通过一个handle对象将requestHandlers中的相应方法与相应的路径对应起来。再通过server.start(router.route, handle)将三者结合在一起。
在项目中,为了代码的可重用性,很多地方我们不采用硬编码的方式把它写死,而是通过函数传参等依赖注入的方式去构造我们的代码,比如这个handle对象,它把相应的路径映射到了对应的处理方法,然后通过参数传递的方式一路传到了route方法中。这样的话,当有新的路径和requestHandlers.js中有新的对应的处理方法时,我们只需要在handle对象中增加相应的路径跟方法就可以了,不需要对route方法进行改写。
而且为了以不阻塞的方式去实现我们的代码,我也用函数传递的方式,将response对象传递到了requestHanlders.js中,在里面的每个函数中调用response.writeHead()、response.write()、response.end()等方法,避免当其中一个函数(start()或upload()或show())消耗比较长的时间的时候会对后面的请求起到阻塞的效果。
这就就是一个简单的基于node.js的web应用的代码的组织形式,希望能帮助大家。阅读全文
0 0
- 构建基于Node.js的web应用
- Node.js项目实践:构建可扩展的Web应用
- Node -- 构建Web应用
- 【深入浅出Node.js系列八】一个基于Node.js完整的Web应用实战
- 从零构建部署Node.js+Express+Bootstrap Web应用
- 基于Node.js的Web框架集合
- 基于Node.js的Web Socket
- 基于node.js的web程序入门
- 基于node.js的构建工具grunt.js
- 基于Node.js的自动化构建工具Grunt.js
- 基于Node.js的后端应用简介
- NodeJS笔记 一个完整的基于Node.js的web应用
- 使用Node.js + Mongodb构建基于Cloud Foundry的项目
- 使用Node.js + Mongodb构建基于Cloud Foundry的项目
- Ghost - 基于 Node.js 构建的开源博客平台
- 在Node.js中构建正确的REST Web服务
- 构建Node.js应用的10个重要的方面
- Node.js入门 ---构建一个Web程序
- ViewDragHelper (三)- 打造仿陌陌视频播放页(深入篇)
- 剑指offer--二叉搜索树与双向链表
- 数据结构-各类树
- 计算机视觉·常用数据集·3D
- 解决:redis添加hash数据失败问题
- 构建基于Node.js的web应用
- Java的内存机制
- HDU 5500 Reorder the Books
- maven生命周期(lifecycle)—— maven权威指南学习笔记(四)
- TCP/IP协议族【第五章IPV4地址】
- 商业web漏扫神器——appscan篇
- 众数问题
- Linux—学习记录_Shell Script脚本
- linux初学之lvm动态扩容管理