第7章 express 框架
7.1 简介
Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架, 提供一系列强大特性帮助你创建各种Web应用。Express 不对 node.js 已有的特性进行二次抽象,我们只是在它之上扩展了Web应用所需的功能。丰富的HTTP工具以及来自Connect框架的中间件随取随用,创建强健、友好的API变得快速又简单
7.2 安装使用
就像一个普通的第三方模块一样安装即可;
npm init
npm install express
var express = require('express');
var app = express();
app.get('/',(req,res)=>{
res.send('hello world !');
})
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
其中 Request、Response -- API 我们需要重点关注
第8章 项目的重构
将我们之前的海贼王项目使用express框架进行重写,重写过程中,学习框架提供的各种API,并完善项目功能;
8.1 启动服务器
创建http.js
var express = require('express');
var app = express();
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
8.2 重写路由模块
之前我们写了一个独立的模块(luyou.js)来处理请求,而在 express 中已经帮我们写好了路由的请求处理规则,不需要我们进行判断;
路由 是指确定应用程序如何响应对特定端点的客户端请求,该请求是URI(或路径)和特定HTTP请求方法(GET,POST等)。
每个路由都可以有一个或多个处理函数,这些函数在路由匹配时执行。
8.2.1 express 中的基本路由
路径定义采用以下结构:
app.method(path, handler)
以下示例定义了简单路由。
Hello World!
在主页上回复:
app.get('/', function (req, res) {
res.send('Hello World!')
})
在根路由(/
),应用程序的主页上响应POST请求:
app.post('/', function (req, res) {
res.send('Got a POST request')
})
响应对/user
路由的PUT请求:
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user')
})
响应对/user
路由的DELETE请求:
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user')
})
8.2.2. 外置路由
设置 外置路由 rout.js
var express = require('express');
var router = express.Router();
router.get('/',(req,res)=>{
res.send('123');
})
router.get('/user',(req,res)=>{
res.send('user');
})
router.post('/edit',(req,res)=>{
res.send('post_edit');
})
// 导出 router
module.exports = router;
写好路由规则,一定要记得将 路由对象(router) 导出
var express = require('express');
var app = express();
// 引入外置路由
var rout = require('./rout');
app.use(rout); // 使用引入外置的路由
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
将外置路由引入后,使用 app.use() 进行加载使用;
8.2.3 使用外置路由修改项目
在 luyou.js 中,注释以前的代码,添加新代码
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router.get('/', (req, res) => {
yewu.getall(function (data) {
res.end(data);
})
})
module.exports = router;
在 http.js 中,使用 express 启动服务,并引入使用新修改的 luyou.js 模块
var express = require('express');
var app = express();
var luyou = require('./luyou');
app.use(luyou);
app.listen('8080',()=>{
console.log('127.0.0.1:8080')
})
8.2.4 使用链式操作添加路由
luyou.js
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
// express的路由支持链式操作
router
.get('/', (req, res) => {
yewu.getall(function (data) {
res.end(data);
})
})
.get('/getuser', (req, res) => {
// req 提供了query属性获取请求参数
var id = req.query.id;
// 在yewu模块中封装方法,传递id参数。
// 使用回掉函数获取数据
yewu.getone(id, function (data) {
res.end(data);
});
})
module.exports = router;
8.2.5 优化路由模块
路由模块 (luyou.js) 中只负责调用,调用时,将req res 传入业务模块
router
.get('/', (req, res) => {
yewu.getall(req,res);
// yewu.getall(function (data) {
// res.end(data);
// })
})
.get('/getuser', (req, res) => {
yewu.getone(req,res);
// req 提供了query属性获取请求参数
// var id = req.query.id;
// // 在yewu模块中封装方法,传递id参数。
// // 使用回掉函数获取数据
// yewu.getone(id, function (data) {
// res.end(data);
// });
})
业务模块接受 req res 负责处理请求并响应数据
getall: function (req,res) {
db.select(function (data) {
// 利用回调函数获取数据
var html_data = template('./index.html', { data: data });
// console.log(html_data);
res.end(html_data);
});
},
getone:function(req,res){
db.where('id='+req.query.id).select(function(data){
var user_data = template('./users.html',{data:data});
res.end(user_data);
});
},
继续简化路由模块
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
原理:
function fn(callback){
var a = 1;
var b = 2;
callback(a,b);
}
var pros = function(a,b){
console.log(a+b);
}
fn(function(a,b){
pros(a,b);
})
// fn(pros);
8.3 重写模板引擎
安装:
npm install --save art-template
npm install --save express-art-template
官方示例:
var express = require('express');
var app = express();
app.engine('art', require('express-art-template'));
app.set('view options', {
debug: process.env.NODE_ENV !== 'production'
});
app.get('/', function (req, res) {
res.render('index.art', {
user: {
name: 'aui',
tags: ['art', 'template', 'nodejs']
}
});
});
修改 http.js 将 express-art-template 注册为express框架的模板引擎,并设置模板后缀为 html
在项目中新建views目录,将所有静态页面放入views目录
8.4 利用 Express 托管静态文件
http://www.expressjs.com.cn/starter/static-files.html
在项目中新建 public 文件夹并将bootstrap.css移入, 修改 index.html 加载 css 静态文件 ,在http.js中引入并设置静态资源加载路径:
如果要使用多个静态资源目录,请多次调用 express.static
函数:
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件时,express.static
函数会根据目录的添加顺序查找所需的文件。
8.5 完成项目重构
修改所有路由及业务模块代码
luyou.js
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post);
module.exports = router;
yewu.js
// var linkdb = require('./linkdb');
var db = require('./db');
var querystring = require('querystring');
// template.defaults.root = './';
module.exports = {
getall: function (req, res) {
db.select(function (data) {
res.render('index.html', { data: data });
});
},
getone: function (req, res) {
db.where('id=' + req.query.id).select(function (data) {
res.render('./users.html', { data: data });
});
},
upuser_get: function (req, res) {
db.where('id=' + req.query.id).select(function (data) {
res.render('./upuser.html', { data: data });
});
},
upuser_post: function (req, res) {
var data = '';
req.on('data', function (che) {
data += che;
})
// 绑定end事件,监听数据接受完成
req.on('end', function () {
// console.log(data);
// 获取Post传输的数据
var post_data = querystring.parse(data);
// 调用数据模块修改用户信息
db.where('id=' + req.query.id).update(post_data, function (changedRows) {
// http服务器相应要求必须是字符串
res.json(changedRows);
})
})
}
}
8.6 展示用户头像
8.7 修改用户头像
8.7.1 测试文件上传
创建服务器:
var express = require('express');
var app = express();
app.post('/upfile',(req,res)=>{
var data = '';
req.on('data', function (che) {
data += che;
});
req.on('end',()=>{
// 直接打印post传过来的数据
console.log(data);
})
})
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
使用postman工具上传文件:
8.7.2 借助第三方插件处理文件上传
https://www.npmjs.com/package/formidable
安装模块 `npm install formidable
`
var express = require('express');
var formidable = require('formidable');
var fs = require('fs');
var app = express();
app.post('/upfile', (req, res) => {
var form = new formidable.IncomingForm();
// form.uploadDir = './img'; // 设置上传路径
// form.keepExtensions = true; // 保留文件扩展名
form.parse(req, function (err, fields, files) {
var times = new Date().getTime();
// 组装上传路径
var file_path = './img/'+times+files.imgs.name;
// 将缓存文件移动至制定目录
fs.rename(files.imgs.path,file_path,(err)=>{
console.log(file_path);
});
res.end();
});
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
8.7.3 在项目中实现文件上传
修改 upuser.html
修改业务模块 yewu.js 使用 formidable 获取 post 数据,实现文件上传
upuser_post: function (req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var times = new Date().getTime();
// 组装上传路径
var file_path = './public/img/' + times + files.imgs.name;
// 将缓存文件移动至指定的public目录
fs.rename(files.imgs.path, file_path, (err) => {
if(!err){
// 因为设置静态资源时,已经时public文件夹,写入数据库时,不要加public
fields.img = './img/' + times + files.imgs.name;
db.where('id=' + req.query.id).update(fields, function (changedRows) {
// http服务器相应要求必须是字符串
res.json(changedRows);
})
}else{
console.log('文件上传失败'+err);
}
});
});
}
8.8 用户登陆
8.8.1 登陆逻辑及cookie-session 的使用
express官方资源中,为我们提供了一个中间件,cookie-session
npm install cookie-session
测试代码:
var express = require('express');
var cookieSession = require('cookie-session')
var app = express();
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['ss'] // 用于加密的关键字
}))
app.get('/', (req, res) => {
// 获取并判断session
if(req.session.sess_data){
res.send('已经登陆')
}else{
// 如果没有session,跳转到登陆页面
res.send('<script>alert("没登陆");window.location.href="/up"</script>');
}
})
app.get('/up', (req, res) => {
// 展示登陆页面,获取用户数据并写入session
req.session.sess_data = {name:12,age:89};
res.send('已写入session');
});
8.8.2 完成项目登陆功能
修改http模块,注册 cookie-session 中间件;
var express = require('express');
var app = express();
var cookieSession = require('cookie-session');
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['xilingzuishuai'] // 用于加密的关键字
}))
在业务模块 (yewu.js) 中 添加逻辑判断,只有登陆后才能展示首页:
getall: function (req, res) {
// 获取并判断session
if (req.session.sess_data) {
db.select(function (data) {
res.render('index.html', { data: data });
});
} else {
// 如果没有session,跳转到登陆页面
res.send('<script>alert("没登陆");window.location.href="/upload"</script>');
// res.send('没登陆');
}
},
在路由模块(luyou.js) 中添加以下两个路由,get 展示静态登陆页面,post 获取用户提交的数据并写入 session ,写入成功后,跳转到首页;在业务模块(yewu.js)中添加响应的方法
.get('/upload',yewu.upload_get)
.post('/upload',yewu.upload_post)
upload_get: (req, res) => {
// 展示登陆页面
res.render('./upload.html', {});
},
upload_post: (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
// console.log(fields);
// 获取用户提交数据,判断用户名密码是否正确
if (fields.userName == "admin" && fields.pwd == "123") {
// 数据正确,写入session
req.session.sess_data = fields;
res.send('<script>alert("登陆成功");window.location.href="/"</script>');
}else{
// 数据错误,重新跳回登陆页面
res.send('<script>alert("登陆失败");window.location.href="/upload"</script>');
}
})
}
第9章 Express的中间件
9.1 什么是中间件
在一个整体的流程中的某个环节,因为某些原因加入了额外的处理环节;
9.2 中间件的使用
9.2.1 应用中间件
语法:
app.use()
app.use(function(){})
无论发送任何请求都会执行的中间件
app.use('/path', function(){})
只要在请求path路由时才会执行的中间件(无论GET/POST)
app.method()
app.get()
在get请求时会执行的中间件
app.post()
在post请求时会执行的中间件
app.use() 的用法
var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
console.log(123);
})
// 应用中间件
// 请求 '/user' 时,会先调用中间件
app.use(function (req, res, next) {
console.log(req);
next();
});
// 调用之前先调用中间件
app.get('/user',function(req,res){
console.log('user');
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
app.method() 的用法
var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
console.log(123);
})
// 应用中间件
// 只有在 post 请求user 时才起作用
app.post('/user',function (req, res, next) {
console.log(req);
next();
});
// 调用之前先调用中间件
// 接受所有请求方式请求user
app.all('/user',function(req,res){
console.log('user');
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
9.2.2 路由中间件
路由器层中间件的工作方式与应用层中间件基本相同,差异之处在于它绑定到 express.Router()
的实例。
使用 router.use()
和 router.METHOD()
函数装入路由器层中间件;
我们之前项目的代码,就是在使用路由中间件:
var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post)
.get('/upload',yewu.upload_get)
.post('/upload',yewu.upload_post)
;
9.2.3 内置中间件
除 express.static
外,先前 Express 随附的所有中间件函数现在以单独模块的形式提供:中间件函数的列表
Express 中唯一内置的中间件函数是 express.static
。此函数基于 serve-static,负责提供 Express 应用程序的静态资源。
对于每个应用程序,可以有多个静态目录:
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));
9.2.4 第三方中间件
使用第三方中间件向 Express 应用程序添加功能。
安装具有所需功能的 Node.js 模块,然后在应用层或路由器层的应用程序中将其加装入。
var cookieSession = require('cookie-session');
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['xilingzuishuai'] // 用于加密的关键字
}))