Node.js开发Web后台服务
2017-03-16 15:06
621 查看
一、简介
Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221143342354-835289664.png)
能方便地搭建响应速度快、易于扩展的网络应用,Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行的数据密集型的实时应用。
官网:https://nodejs.org/en/
中文:https://cnodejs.org/ 、http://nodejs.cn/
API:http://nodeapi.ucdok.com/#/api/
简单说Node.js就是运行在服务器端的JavaScript,是现在流行的语言中能同时运行在前端与后台的程序语言,你可以把JavaScript想像成Java与C#。相关技术:
数据库:MongoDB,非关系型数据库,NoSQL(Not only SQL)
MVC框架:AngularJS
Web服务器:Express
模板引擎:jade、ejs、htmljs、swig、hogan.js
二、搭建Node.js开发环境
2.1、安装Node.js
去官网下下载最新版本的Node.js一步一步按提示安装即可,如果安装失败就手动安装,将Node.js的安装位置配置到环境变量的path中。![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221145302776-1473642599.png)
安装完成后启动命令行,测试:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221145631292-604381565.png)
2.2、安装IDE开发Node.js插件
如果不使用IDE开发项目效率较低,在很多主流的集成开发环境(IDE)中都可以安装插件支持Node.js开发,如Eclipse,这里我们以HBuilder为例:启动HBuilder->工具->插件安装
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221145946198-2017554387.png)
安装成功后就可以新建Node.js项目了:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221150122276-117101583.png)
这里选择Hello World,新建好的项目如下:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221150420932-1194438541.png)
hello-world-server.js文件就是一个简单的web服务器,右键选择“运行方式”->"Node Application"
控制台提示“Server running at http://127.0.0.1:1337/”在浏览器查看的效果如下:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221150744167-419963852.png)
三、第一个Node.js程序
在上面的示例中,我们是通过IDE完成编译与运行的,其实手动运行也可以,比如编写一段代码如下:server.js
在node环境下解释运行:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221155949714-1496765865.png)
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161221160021167-1270760207.png)
使用node执行js:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222095627698-863156491.png)
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222095652042-170402518.png)
5.5、使用Nodeclipse开发Express项目
如果直接使用记事本效率会不高,nodeclipse插件可以方便的创建一个Express项目,步骤如下:![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222095037417-402222278.png)
创建好的项目如下:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222101411276-1040342301.png)
app.js是网站:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var app = express(); //指定视图引擎为ejs app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
bin\www是web服务器:
#!/usr/bin/env node /** * 依赖模块,导入 */ var app = require('../app'); var debug = require('debug')('nodejsexpress:server'); var http = require('http'); /** * 从上下文环境中获得监听端口,如果空则3000 */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * 创建Web服务器 */ var server = http.createServer(app); /** * 开始监听 */ server.listen(port); server.on('error', onError); //指定发生错误时的事件 server.on('listening', onListening); //当监听成功时的回调 /** * 规范化端口 */ function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** *错误事件监听 */ function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; //错误处理 switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); //结束程序 break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } /** * 当用户访问服务器成功时的回调 */ function onListening() { var addr = server.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
routers/index.js路由,有点类似控制器或Servlet:
var express = require('express'); var router = express.Router(); /* 获得首页 */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
views/index.ejs首页视图:
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body> </html>
在www上右键选择“运行方式”->“Node Application”运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222102937776-605885718.png)
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222103103417-546409240.png)
5.6、ejs基础
ejs是一个Express Web应用的模板引擎,在NodeJS开发中可以选择的模板引擎可能是所有Web应用开发中范围最广的,如jade、ejs、htmljs、swig、hogan.js,但ejs是最容易上手的,与jsp,asp,php的原始模板引擎风格很像。官网:http://www.embeddedjs.com/
添加一个product.js路由:
var express = require('express'); var router = express.Router(); /* 产品 */ router.get('/', function(req, res, next) { var products=[]; products.push({name:"ZTE U880",price:899.8}); products.push({name:"HuWei 荣耀8",price:1899.8}); products.push({name:"iPhone 7 Plus 128G",price:5899.8}); //将product视图与指定的对象渲染后输出到客户端 res.render('product', { title: '天狗商城', pdts:products}); }); module.exports = router;
在views目录下添加product.ejs视图,这里是一个简单的MVC:
<!DOCTYPE html> <html> <head> <title> <%= title %> </title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %> - 产品列表</h1> <table border="1" width="80%"> <tr> <th>序号</th> <th>名称</th> <th>价格</th> </tr> <%pdts.forEach(function(pdt,index){%> <tr> <td> <%=index+1%> </td> <td> <%=pdt.name%> </td> <td> <%=pdt.price%> </td> </tr> <%});%> </table> <ul> <% for(var i=0; i<pdts.length; i++) {%> <li> <%=pdts[i].name%> </li> <% } %> </body> </html>
修改app,注册定义好的模块product:
var index = require('./routes/index'); var users = require('./routes/users'); var pdts = require('./routes/product'); var app = express(); //指定视图引擎为ejs app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); app.use('/pdt', pdts);
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222111322307-1520056404.png)
5.7、lodash
这是一个具有一致接口、模块化、高性能等特性的 JavaScript 工具库。可以非常方便的操作json。官网:http://lodashjs.com/
安装:
npm i -g npm
npm i --save lodash
安装时先用cd切换到当前项目下。
如果浏览器使用可以直接引入:
<script src="lodash.js"></script>
后台Node.js使用,可以引入模块:
//导入lodash模块 var _= require('lodash'); var products=[]; products.push({name:"ZTE U880",price:899.8}); products.push({name:"HuWei 荣耀8",price:1899.8}); products.push({name:"iPhone 7 Plus 128G",price:5899.8}); //1、取出第一个元素 var obj1=_.first(products); console.log(obj1.name); //ZTE U880 //2、取出最后一个元素 var obj2=_.last(products); console.log(obj2.name); //iPhone 7 Plus 128G //3、指定查找条件返回符合条件的索引 var obj3=_.findIndex(products,function(obj){ return obj.price>=1000&&obj.name.indexOf("7")>0; }); console.log(obj3); //2 //4、指定查找条件返回查找到的对象 var obj4=_.find(products,function(obj){ return obj.price>=1000&&obj.name.indexOf("7")>0; }); console.log(obj4); //{ name: 'iPhone 7 Plus 128G', price: 5899.8 } //5、排序 var obj5=_.orderBy(products,["price","name"],["desc","asc"]); console.log(obj5); //[ { name: 'iPhone 7 Plus 128G', price: 5899.8 }, //{ name: 'HuWei 荣耀8', price: 1899.8 }, //{ name: 'ZTE U880', price: 899.8 } ] //6、查找价格为1899.8的产品的key var obj6=_.findKey(products,{price:1899.8}); console.log(obj6); //1
API的使用非常简单,但需要注意版本,可以现查现用,API地址:https://lodash.com/docs/4.17.2
5.8、参数
5.8.1、URL中的参数占位
Checks route params (req.params), ex: /user/:id127.0.0.1:3000/index,这种情况下,我们为了得到index,我们可以通过使用req.params得到,通过这种方法我们就可以很好的处理Node中的路由处理问题,同时利用这点可以非常方便的实现MVC模式;
//获得产品根据Id router.get('/:id/:category',function(request,res,next){ res.send(request.params.id+","+request.params.category); });
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222144548511-613760100.png)
5.8.2、URL中的QueryString
Checks query string params (req.query), ex: ?id=12127.0.0.1:3000/index?id=12,这种情况下,这种方式是获取客户端get方式传递过来的值,通过使用req.query.id就可以获得,类似于PHP的get方法;
router.get('/:id',function(request,res,next){ res.send("name:"+request.query.name); });
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222150043479-170783647.png)
5.8.3、HTTP正文中的参数
在post请求中获得表单中的数据。Checks urlencoded body params (req.body), ex: id=
127.0.0.1:300/index,然后post了一个id=2的值,这种方式是获取客户端post过来的数据,可以通过req.body.id获取,类似于PHP的post方法;
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222150412682-243382356.png)
页面:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<!DOCTYPE html> <html> <head> <title> <%= title %> </title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %> - 产品列表</h1> <table border="1" width="80%"> <tr> <th>序号</th> <th>名称</th> <th>价格</th> </tr> <%pdts.forEach(function(pdt,index){%> <tr> <td> <%=index+1%> </td> <td> <%=pdt.name%> </td> <td> <%=pdt.price%> </td> </tr> <%});%> </table> <ul> <% for(var i=0; i<pdts.length; i++) {%> <li> <%=pdts[i].name%> </li> <% } %> </ul> <p> <%if(typeof msg!="undefined"){%> <%=msg%> <%}%> </p> <form action="pdt/add" method="post"> <p> 名称:<input name="name" /> </p> <p> 价格:<input name="price" /> </p> <button>添加</button> </form> </body> </html>
View Code
代码:
router.post('/add',function(request,res,next){ var entity={name:request.body.name,price:request.body.price}; products.push(entity); //将product视图与指定的对象渲染后输出到客户端 res.render('product', { title: '天狗商城', pdts:products,msg:"添加成功"}); });
结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222151340745-1374859267.png)
5.9、JSON
如果需要Node.js向外提供返回JSON的接口,Express也是非常方便的,可以使用原来在浏览器中使用到的JSON对象,这是一个浏览器内置对象在服务可以直接使用:将对象序列化成字符:
//对象 var rose={"name":"Rose","weight":"65"}; //序列化成字符串 var str=JSON.stringify(rose); alert(str);
结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222151858839-27300214.png)
反序列化,将字符转换成对象:
//将字符串转换成JavaScript对象 var markStr='{"name":"mark","weight":"188"}'; var mark=JSON.parse(markStr); alert(mark.name+","+mark.weight);
结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222152634417-1260078212.png)
Express已经封装了一个json方法,直接调用该方法就可以序列化对象:
/* 产品 */ router.get('/rest', function(req, res, next) { res.json(products); });
运行结果:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161222152914276-1587553101.png)
练习:完成一个图书管理的功能,图书包含(编号,名称,作者,图片,价格),实现:
a)、非AJAX的CRUD,使用Node.js+Express+ejs的动态技术。
b)、AJAX的CRUD,使用Node.js+Express+jQuery+HTML技术实现。
c)、使用RestFul风格的服务完成第个作业,get,post,delete,put请。
六、RESTful(表述性状态转移)
REST是英文Representational State Transfer的缩写,中文称之为“表述性状态转移”基于HTTP协议
是另一种服务架构
传递是JSON、POX(Plain Old XML)而不是SOAP格式的数据
充分利用HTTP谓词(Verb)
侧重数据的传输,业务逻辑交给客户端自行处理
REST是一种分布式服务架构的风格约束,像Java、.Net(WCF、WebAPI)都有对该约束的实现,使URL变得更加有意义,更加简洁明了,如:
http://www.zhangguo.com/products/1 get请求 表示获得所有产品的第1个
http://www.zhangguo.com/products/product post请求 表示添加一个产品
http://www.zhangguo.com/products/1/price get请求 表示获得第1个产品的价格
http://www.zhangguo.com/products/1 delete请求 删除编号为1的产品
REST设计需要遵循的原则
网络上的所有事物都被抽象为资源(resource);
每个资源对应一个唯一的资源标识符(resource identifier);
通过通用的连接器接口(generic connector interface)对资源进行操作;
对资源的各种操作不会改变资源标识符;
所有的操作都是无状态的(stateless)
谓词
GET
表示查询操作,相当于Retrieve、Select操作
POST
表示插入操作,相当于Create,Insert操作
PUT
表示修改操作,相当于Update操作
DELETE
表示删除操作,相当于Delete操作
其它还有:
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223101344557-1727952968.png)
NodeJS+Express可以很容易的实现REST
application/x-www-form-urlencoded
multipart/form-data
application/json
res.setHeader('Content-Type', 'application/json;charset=utf-8');
示例代码cars.js:
var express = require('express'); var router = express.Router(); var _= require('lodash'); var cars=[]; cars.push({id:201701,name:"BMW",price:190,speed:"210km/h",color:"白色"}); cars.push({id:201702,name:"BYD",price:25,speed:"160km/h",color:"红色"}); cars.push({id:201703,name:"Benz",price:300,speed:"215km/h",color:"蓝色"}); cars.push({id:201704,name:"Honda",price:190,speed:"170km/h",color:"黑色"}); cars.push({id:201705,name:"QQ",price:130,speed:"210km/h",color:"白色"}); /* Get */ /*获得所有汽车*/ /*url /cars/*/ router.get('/', function(req, res, next) { res.json(cars); }); /*Get*/ /*获得汽车通过id*/ /*url:/cars/:id */ router.get('/:id', function(req, res, next) { //从路径中映射参数,转换成数字 var id=parseInt(req.params.id); var car=_.find(cars,{id:id}); res.json(car); }); /*Post*/ /*添加汽车*/ /*url:/cars/car */ router.post('/car', function(req, res, next) { var car=req.body; //从请求正文中获得json对象 car.id=_.last(cars).id+1; //将编号修改为最后一辆车的编号+1 cars.push(car); //将汽车对象添加到集合中 res.json(car); //将添加成功的车以json的形式返回 }); /*Put*/ /*修改汽车*/ /*url:/cars/car */ router.put('/car', function(req, res, next) { var car=req.body; //从请求正文中获得json对象 console.log(req.body); var index=_.findIndex(cars,{id:parseInt(car.id)}); //根据id获得车在集合中的下标 cars[index]=car; //替换原对象 //res.json(car); //将修改后的车以json的形式返回 res.send({status:"success", message:"更新成功!"}); }); /*Delete*/ /*删除汽车*/ /*url:/cars/:id */ router.delete('/id/:id', function(req, res, next) { //获得url中的编号参数 var id=parseInt(req.params.id); var index=_.findIndex(cars,{id:id}); //根据id获得车在集合中的下标 cars.splice(index,1); //在cars数组中删除下标从index开始的1条数据 res.send({status:"success", message:"删除成功!"}); }); module.exports = router;
示例代码app.js:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var pdts = require('./routes/product'); var task = require('./routes/task'); var cars = require('./routes/cars'); var app = express(); //指定视图引擎为ejs app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); app.use('/pdt', pdts); app.use("/task",task); app.use("/cars",cars); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
/* Get */
/*获得所有汽车*/
/*url /cars/*/
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223100830448-2076876806.png)
/*Get*/
/*获得汽车通过id*/
/*url:/cars/:id */
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223101030042-1011736087.png)
/*Post*/
/*添加汽车*/
/*url:/cars/car */
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223103714276-2008195573.png)
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223103745714-149868509.png)
参数中的json格式一定要使用标准格式,注意引号,注意Content-Type,默认的Content-Type类型是:application/x-www-form-urlencoded
/*Put*/
/*修改汽车*/
/*url:/cars/car */
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223104653026-375693413.png)
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223104632151-1638726484.png)
/*Delete*/
/*删除汽车*/
/*url:/cars/:id */
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223105104432-1823460558.png)
![](http://images2015.cnblogs.com/blog/63651/201612/63651-20161223105030745-492987203.png)
七、示例下载
git:https://coding.net/u/zhangguo5/p/NodeJS001/gitgit:https://coding.net/u/zhangguo5/p/NodeJSExpress/git
相关文章推荐
- Node.js开发web后台入门
- 利用OpenShift托管Node.js Web服务进行微信公众号开发
- Node.js实现WEB 服务开发
- 用Node.js来开发后台服务
- 为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务
- 门外汉学Web开发-WordPress代码导读(4-Admin后台模块之JS)
- node.js WEB开发中图片验证码的实现方法
- node.js web服务器开发 实践(一) 收获
- Node.js首选web开发框架-Express.js基础教程
- node.js WEB开发中图片验证码的实现方法
- node.js适合游戏后台开发吗?
- js调用web服务的接口和调用后台方法
- Node.js Web 开发框架大全《静态文件服务器篇》
- 教你如何选择Node.js Web开发框架?
- Node.js企业开发: 五服务监控
- 购物平台webApp+服务后台开发
- 了不起的Node.js: 将JavaScript进行到底(Web开发首选,实时,跨多服务器,高并发)
- Node.js Web 开发框架大全《路由篇》
- [译] 第二十七天:Restify - 用Node.js创建正确的REST Web服务
- Node.js Web 开发框架大全《中间件篇》