Express 是一个简洁而灵活、目前最流行的基于Node.js的Web开发框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具;
可以快速地搭建一个完整功能的网站;使用Node.js作为AngularJS开发Web服务器的最佳方式是使用Express模块;
可以设置中间件来响应 HTTP 请求。
定义了路由表用于执行不同的 HTTP 请求动作。
可以通过向模板传递参数来动态渲染 HTML 页面。
丰富的 HTTP 快捷方法和任意排列组合的 Connect 中间件,让你创建健壮、友好的 API 变得既快速又简单。
Express 不对 Node.js 已有的特性进行二次抽象,我们只是在它之上扩展了 Web 应用所需的基本功能。
安装 Express 并将其保存到依赖列表中:npm install express --save(也可以不加后面的 --save)如图所示:
(1)首先创建 bin 目录;再创建一个app.js文件作为一个入口,用于中间件挂载;再创建一个www.js文件作为启动服务,如下所示:
app.js 文件代码如下:
//引入express模块
var express = require('express');
//创建一个app对象,作为一个express模块导出的入口函数,类似一个web 应用(网站)
var app = express();
//接受指定路径的请求,指定回调函数
app.get('/', function (req, res) {
// 返回客户端的字符
res.send('Hello Express!');
});
// 导出对象
module.exports=app;
www.js 文件代码如下:
// 导入自定义模块
const app=require("../app.js")
// 绑定并侦听指定主机和端口上的连接。
app.listen(8080,"127.0.0.1",()=>{
console.log("web应用运行中…");
});
(2)在package.json文件中,添加一个 ‘ start ’ 的参数,来访问该文件
一个请求发送到服务器后,它的生命周期是 先收到request(请求),然后服务端处理,处理完了以后发送response(响应)回去,而这个服务端处理的过程就有文章可做了,想象一下当业务逻辑复杂的时候,为了明确和便于维护,需要把处理的事情分一下,分配成几个部分来做,而每个部分就是一个中间件。简而言之就是:运行程序时,有时候会出现卡住的情况,此时我们使用中间件就可以解决运行卡,一直在转的情况了!!!
use 是express注册中间件的方法,它返回一个函数。app.use([path,], function [, function…]) 挂载中间件方法到路径上。如果路径未指定,那么默认为”/”
// 导入内置模块
var express = require('express');
// 是一个express模块导出的入口函数,类似一个web 应用(网站)
var app = express();
// 中间件
app.use(function (req, res, next) {
console.log('当前时间:', Date.now())
next()
})
// 接受指定路径的请求,指定回调函数
app.get('/', function(req, res){
res.send('测试use中间件栗子!'); // 返回客户端的字符
});
// 绑定并侦听指定主机和端口上的连接。
module.exports=app; // 导出对象
中间件是一个函数,在响应发送之前对请求进行一些操作,这个函数有些不太一样,它还有一个next参数,而这个next也是一个函数,它表示函数数组中的下一个函数,如果当前中间件函数没有结束请求/响应循环,那么它必须调用 next(),以将控制权传递给下一个中间件函数。否则,请求将保持挂起状态。意思就是说:在这个函数体里面,写完逻辑之后,如果还想执行下面的方法,就必须在use函数体里面写上 next() 方法来继续执行下一行代码!中间件方法是顺序处理的。
一个路由将匹配任何路径如果这个路径以这个路由设置路径后紧跟着 ” / ”。比如:app.use(‘ /apple ’,回调函数)将匹配”/apple”,”/apple/images”,”/apple/images/news”等
在一个路径上挂载一个中间件之后,每当请求的路径的前缀部分匹配了这个路由路径,那么这个中间件就会被执行。 由于默认的路径为/,中间件挂载没有指定路径,那么对于每个请求,这个中间件都会被执行,中间件在请求方法的前面。
栗子如下:
在Express程序中使用express.static中间件。 为程序托管位于程序目录下的public目录下的静态资源,栗子如下:
// 导入内置模块
var express = require('express');
var path = require('path'); // 表示路径的模块
var app = express();
// 中间件挂载静态资源
// __dirname:当前目录,连接public目录
// 完整的路径为:file:///E:/qd/11.%20node.js/%E6%A1%88%E4%BE%8B/ch03/public/index.html
app.use(express.static(path.join(__dirname,"public")))
app.get('/', function(req, res){
res.send('测试中间件挂载静态资源的栗子!'); // 返回客户端的字符
});
// 导出对象
module.exports=app;
route():避免重复路线名称,栗子如下:
(1)首先创建一个router目录,在目录下面创建一个index.js文件,代码如下:
var express = require('express')
// 创建路由对象
var router = express.Router()
// 添加访问路径
router.get('/', function (req, res, next) {
console.log("hello router!");
next()
})
// 导出路由
module.exports=router;
(2)创建一个router.js 文件作为web服务器,挂载路由,运行到客户端(浏览器)上,代码如下:
var express = require('express')
var app = express();
// 导入路由
var indexRouter = require("./router/index");
var carRouter = require("./router/car");
// 挂载路由
app.use("/",indexRouter);
app.use("/car",carRouter);
// 添加访问路径
app.get('/', function(req, res){
res.send('测试路由的栗子!'); // 返回客户端的字符
});
// 导出对象
module.exports=app;
请注意:如果有多个路由,路径会发生变化!!栗子如下:
在 router 目录下面创建一个 car.js 文件,代码如下:
const express = require('express')
// 创建路由对象
const router = express.Router()
// 添加访问路径
router.get('/', function (req, res, next) {
res.send("hello car!");
next()
})
// 导出路由
module.exports=router;
router.js 文件 代码如下:
var express = require('express')
var app = express();
// 导入路由
var indexRouter = require("./router/index");
var carRouter = require("./router/car");
// 挂载路由
app.use("/",indexRouter);
app.use("/car",carRouter);
// 添加访问路径
app.get('/', function(req, res){
res.send('测试路由的栗子!'); // 返回客户端的字符
});
// 导出对象
module.exports=app;
(1)首先导入依赖:npm i morgan
(2)导入第三方模块
(3)指定格式,挂载日志组件
morgan格式如下:
combined:Standard Apache combined log output. 标准的Apache结合日志输出格式
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
common:Standard Apache common log output.标准的Apache通用日志输出格式
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]
dev:Concise output colored by response status for development use. The :status
token will be colored green for success codes, red for server error codes, yellow for client error codes, cyan for redirection codes, and uncolored for information codes.为开发者使用的彩色输出状态,如果成功则状态标记为绿色,红色为服务器端错误代码,黄色为客户端错误代码,青色为重定向代码,没有使用彩色的表示普通信息。
:method :url :status :response-time ms - :res[content-length]
short:Shorter than default, also including response time. 比默认更短且包含响应时间的日志格式
:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
tiny:The minimal output. 最小的日志输出格式
:method :url :status :res[content-length] - :response-time ms
具体操作代码如下:
//引入express模块
var express = require('express');
//创建一个app对象,作为一个express模块导出的入口函数,类似一个web 应用(网站)
var app = express();
var logger = require("morgan");
// 挂载日志组件
app.use(logger("dev"));
//接受指定路径的请求,指定回调函数
app.get('/', function (req, res) {
// 返回客户端的字符
res.send('测试morgan日志的栗子!!');
});
// 导出对象
module.exports=app;
通过应用生成器工具 express-generator
可以快速创建一个应用的骨架。(包含bin、public、routes、views目录;json、js文件等等)
通过 npx
命令来运行 Express 应用程序生成器。
npx express-generator -e
对于较老的 Node 版本,请通过 npm 将 Express 应用程序生成器安装到全局环境中并使用:
$ npm install -g express-generator
$ express -e
所有可用的命令行参数:
$ express -h
Usage: express [options] [dir]
Options:
-h, --help 输出使用方法
--version 输出版本号
-e, --ejs 添加对 ejs 模板引擎的支持
--hbs 添加对 handlebars 模板引擎的支持
--pug 添加对 pug 模板引擎的支持
-H, --hogan 添加对 hogan.js 模板引擎的支持
--no-view 创建不带视图引擎的项目
-v, --view <engine> 添加对视图引擎(view) <engine> 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
-c, --css <engine> 添加样式表引擎 <engine> 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
--git 添加 .gitignore
-f, --force 强制在非空目录下创建
具体操作如下:
(1)创建一个空目录
(2)在命令提示符切换盘符,进入该文件夹路径
(3)使用 npx express-generator 命令生成项目
(3)此时我们发现目录里面是没有 modules 依赖的,所以我们要使用说明提示我们安装依赖的命令 :npm install
(4)最后用上面提示我们运行服务的命令:SET DEBUG=ch04:* & npm start
(5)运行结果:
ejs 是一个Express Web应用的模板引擎,在NodeJS开发中可以选择的模板引擎可能是所有Web应用开发中范围最广的,如jade、ejs、htmljs、swig、hogan.js,但ejs是最容易上手的,与jsp,asp,php的原始模板引擎风格很像。
添加一个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:
<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>
修改app.js 文件,注册定义好的模块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);
运行结果:
路由:
const express = require('express');
const _ = require("lodash")
const router = express.Router();
// 数据
let book = [
{"id":1,"name":"Java高级编程","author":"大强","price":56.9},
{"id":2,"name":"ES6编程","author":"刘静","price":20.0},
{"id":3,"name":"MySQL数据库","author":"张三","price":85.9},
{"id":4,"name":"Vue全家桶","author":"李四","price":72.9},
{"id":5,"name":"SpingBoot","author":"旺旺","price":16.0}
]
// 中间件(查询)
router.get("/",(req,res,next)=>{
// render():该方法将返回可能的错误和呈现的字符串,但不执行自动响应。 当发生错误时,该方法会在内部调用 next (err)。
res.render("index",{book,msg:"",bookData:{id:"",name:"",author:"",price:""}});
});
// 中间件(删除)
router.get("/del/:id",(req,res,next)=>{
// 查找客户端发送过来的id
// _.findIndex方法:该方法返回第一个通过 predicate 判断为真值的元素的索引值(index)
let index = _.findIndex(book,{id:parseInt(req.params.id)})
// console.log(index); // 输出下标
book.splice(index,1);
res.render("index",{book,msg:"删除成功!",bookData:{id:"",name:"",author:"",price:""}});
});
// 中间件(添加)
router.post("/add",(req,res,next)=>{
// req.body:获取客户端post请求过来的数据
let inputText = req.body;
// 自动添加id
// 根据编号排序
let ids = \_.orderBy(book,\["id"\]);
// 获取到book数组的最后一个元素的编号 + 1
inputText.id = \_.last(ids).id + 1;
// 把获取到的json对象添加到book数组中
book.push(inputText);
// 返回给客户端
res.render("index",{book,msg:"添加成功!",bookData:{id:"",name:"",author:"",price:""}});
});
// 中间件(点击编辑把该行的数据渲染到对应的文本框中)
router.get("/edit/:id",(req,res,next)=>{
// _.find方法:遍历 collection(集合)元素,返回 predicate(断言函数)第一个返回真值的第一个元素。
// id需要强制转换为int类型
let bookData = _.find(book,{id:parseInt(req.params.id)});
// 把查询出来的数据(bookData),返回给客户端
res.render("index",{book,bookData,msg:""});
});
// 中间件(修改)
router.post("/update",(req,res,next)=>{
// 获取提交过来的数据对象
let inputText = req.body;
// 在集合中查找要更新的元素,根据id
let updateData = \_.find(book,{id:parseInt(inputText.id)});
// 更新数据
updateData.name = inputText.name;
updateData.author = inputText.author;
updateData.price = inputText.price;
console.log(book);
// 重新渲染页面,更新列表的数据
res.render("index",{book,msg:"修改成功!",bookData:{id:"",name:"",author:"",price:""}});
});
module.exports=router;
ejs 视图: