一、art-template 补充
1.模板优化(引入)
1.1模板引入
{{ include './header.html' }}
<h1>hello<h1>
{{ include './footer.html' }}
在页面中可以抽取除公共的部分,比如 header.html 、footer.html
然后在各个页面中引入我们的公共部分
1.2模板继承
- home.html页面布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{block 'title'}}默认标题{{/block}}</title>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
{{block 'head'}}{{/block}}
</head>
<body>
{{include '../_partials/header.html'}}
{{block 'body'}}{{/block}}
{{include '../_partials/footer.html'}}
<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/bootstrap/dist/js/bootstrap.js"></script>
{{block 'script'}}{{/block}}
</body>
</html>
- 继承home.html页面
{{extend './_layouts/home.html'}}
- home.html页面中使用block预留待填充的 空位置;如:
声明一个坑
{{block 'title'}}{{'多人博客 - 首页'}}{{/block}}
- 在子页面进行继承和个性化填充
{{extend './_layouts/home.html'}} // 继承
{{block 'title'}}{{'多人博客 - 首页'}}{{/block}} // 填充
{{block 'body'}} // 个性化填充
<section class="container">
<ul class="media-list">
....
</ul>
<nav aria-label="Page navigation">
...
</nav>
</section>
{{/block}}
{{block 'script'}}
<script type="text/javascript">
....
</script>
{{/block}}
和python flask 中的jinja2模板语法如出一辙
二、插播,特别的礼物
win 10可以建多个桌面(哈哈哈。特别的礼物)
win + tab
三、重定向针对异步请求无效
客户端收到成功标识,需要自己处理
四、MD5加密
使用 blueimp-md5
var md5 = require('blueimp-md5') // md5
// 对密码进行 md5 重复加密
body.password = md5(md5(body.password))
五、express-session
Express框架中,默认不支持 Session 和 Cookie。我们可以使用第三方中间件:express-session 来解决
- 1.npm install express-session
- 2.配置 (一定要在 app.use(router) 之前)
- 3.使用
当把这个插件配置好之后,我们就可以通过 req.session 来发访问和设置 Session 成员了- 添加 Session 数据:req.session.foo = ‘bar’
- 访问 Session 数据:req.session.foo
var session = require('express-session')
app.use(session({
// 配置加密字符串,它会在原有加密基础之上和这个字符串拼起来去加密,目的是为了增加安全性,防止客户端恶意伪造。盐值加密
secret: 'jjhjjhjhsdu',
resave: false,
saveUninitialized: false // 无论你是否使用 Session ,我都默认直接给你分配一把钥匙
}))
六、express 中间件
七、案例代码
1.项目结构
|—— app.js 项目入口文件
|—— controllers
|—— models 存储使用mongoose设计的数据模型
|—— node_modules 第三方包
|—— package.json 包描述文件
|—— package-lock.json 第三方包版本锁定文件(npm v5以后)
|—— public 静态资源
|—— README.md 项目说明文档
|—— routes 路由文件包(相当于java controller)
|____ views 存储视图目录
2. 项目入口
- app.js
var express = require('express')
var path = require('path')
var bodyParser = require('body-parser')
var session = require('express-session')
var router = require('./router')
var app = express()
app.use('/public/', express.static(path.join(__dirname, './public/')))
app.use('/node_modules/', express.static(path.join(__dirname, './node_modules/')))
// 在 Node 中,有很多第三方模板引擎都可以使用,不是只有 art-template
// ejs、jade(pug)、handlebars、nunjucks
// <%%>
// {{}}
// h1
// div
// h1
app.engine('html', require('express-art-template'))
app.set('views', path.join(__dirname, './views/')) // 默认就是 ./views 目录
// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
// 在 Express 这个框架中,默认不支持 Session 和 Cookie
// 但是我们可以使用第三方中间件:express-session 来解决
// 1. npm install express-session
// 2. 配置 (一定要在 app.use(router) 之前)
// 3. 使用
// 当把这个插件配置好之后,我们就可以通过 req.session 来发访问和设置 Session 成员了
// 添加 Session 数据:req.session.foo = 'bar'
// 访问 Session 数据:req.session.foo
app.use(session({
// 配置加密字符串,它会在原有加密基础之上和这个字符串拼起来去加密
// 目的是为了增加安全性,防止客户端恶意伪造
secret: 'itcast',
resave: false,
saveUninitialized: false // 无论你是否使用 Session ,我都默认直接给你分配一把钥匙
}))
// 把路由挂载到 app 中
app.use(router)
app.listen(5000, function () {
console.log('running...')
})
3.路由架构
- userRouter.js
var express = require('express')
var User = require('./models/user')
var md5 = require('blueimp-md5') // md5
var router = express.Router()
router.get('/', function (req, res) {
// console.log(req.session.user)
res.render('index.html', {
user: req.session.user
})
})
router.get('/login', function (req, res) {
res.render('login.html')
})
router.post('/login', function (req, res) {
// 1. 获取表单数据
// 2. 查询数据库用户名密码是否正确
// 3. 发送响应数据
var body = req.body
User.findOne({
email: body.email,
password: md5(md5(body.password))
}, function (err, user) {
if (err) {
return res.status(500).json({
err_code: 500,
message: err.message
})
}
// 如果邮箱和密码匹配,则 user 是查询到的用户对象,否则就是 null
if (!user) {
return res.status(200).json({
err_code: 1,
message: 'Email or password is invalid.'
})
}
// 用户存在,登陆成功,通过 Session 记录登陆状态
req.session.user = user
res.status(200).json({
err_code: 0,
message: 'OK'
})
})
})
router.get('/register', function (req, res) {
res.render('register.html')
})
router.post('/register', function (req, res) {
// 1. 获取表单提交的数据
// req.body
// 2. 操作数据库
// 判断改用户是否存在
// 如果已存在,不允许注册
// 如果不存在,注册新建用户
// 3. 发送响应
var body = req.body
User.findOne({
$or: [{ // mongodb的条件查找or固定语法
email: body.email
},
{
nickname: body.nickname
}
]
}, function (err, data) {
if (err) {
return res.status(500).json({
success: false,
message: '服务端错误'
})
}
// console.log(data)
if (data) {
// 邮箱或者昵称已存在
return res.status(200).json({
err_code: 1,
message: 'Email or nickname aleady exists.'
})
return res.send(`邮箱或者密码已存在,请重试`)
}
// 对密码进行 md5 重复加密
body.password = md5(md5(body.password))
new User(body).save(function (err, user) {
if (err) {
return res.status(500).json({
err_code: 500,
message: 'Internal error.'
})
}
// 注册成功,使用 Session 记录用户的登陆状态
req.session.user = user
// Express 提供了一个响应方法:json
// 该方法接收一个对象作为参数,它会自动帮你把对象转为字符串再发送给浏览器
res.status(200).json({
err_code: 0,
message: 'OK'
})
// 服务端重定向只针对同步请求才有效,异步请求无效
// res.redirect('/')
})
})
})
router.get('/logout', function (req, res) {
// 清除登陆状态
req.session.user = null
// 重定向到登录页
res.redirect('/login')
})
// router.post('/register', async function (req, res) {
// var body = req.body
// try {
// if (await User.findOne({ email: body.email })) {
// return res.status(200).json({
// err_code: 1,
// message: '邮箱已存在'
// })
// }
// if (await User.findOne({ nickname: body.nickname })) {
// return res.status(200).json({
// err_code: 2,
// message: '昵称已存在'
// })
// }
// // 对密码进行 md5 重复加密
// body.password = md5(md5(body.password))
// // 创建用户,执行注册
// await new User(body).save()
// res.status(200).json({
// err_code: 0,
// message: 'OK'
// })
// } catch (err) {
// res.status(500).json({
// err_code: 500,
// message: err.message
// })
// }
// })
module.exports = router
- topicRouter.js
// 新建话题、删除话题、修改话题、查看话题列表。。。。
const express = require("express");
const router = express.Router();
router.get("/",function (req,res) {
res.send("话题首页页")
});
router.get("/add",function (req,res) {
res.send("添加首页")
});
router.get("/delete",function (req,res) {
res.send("删除话题")
});
module.exports = router;
- router.js
const express = require("express");
const router = express.Router();
//相当于后台的路由,所有的后台处理都需要从这里经过
const user = require("./routes/user");
const topic = require("./routes/topic");
router.use("/user",user);
router.use("/topic",topic);
module.exports = router;
4.mogoose对象
- user.js
var mongoose = require('mongoose')
// 连接数据库
mongoose.connect('mongodb://192.168.43.49/test', { useMongoClient: true })
var Schema = mongoose.Schema
var userSchema = new Schema({
email: {
type: String,
required: true
},
nickname: {
type: String,
required: true
},
password: {
type: String,
required: true
},
created_time: {
type: Date,
// 注意:这里不要写 Date.now() 因为会即刻调用
// 这里直接给了一个方法:Date.now
// 当你去 new Model 的时候,如果你没有传递 create_time ,则 mongoose 就会调用 default 指定的Date.now 方法,使用其返回值作为默认值
default: Date.now
},
last_modified_time: {
type: Date,
default: Date.now
},
avatar: {
type: String,
default: '/public/img/avatar-default.png'
},
bio: {
type: String,
default: ''
},
gender: {
type: Number,
enum: [-1, 0, 1],
default: -1
},
birthday: {
type: Date
},
status: {
type: Number,
// 0 没有权限限制
// 1 不可以评论
// 2 不可以登录
enum: [0, 1, 2],
default: 0
}
})
module.exports = mongoose.model('User', userSchema)
七、express框架Rest风的路由用法
//动态路由 http://localhost:8001/newscontent/1243
app.get('/newscontent/:aid',function (req,res) {
//req.params获取动态路由的传值
var aid = req.params.aid; //aid = 1243
res.send("hello newscontent"+"------"+aid);
})
八、中间件
中间件在 Node.js 中被广泛使用,它泛指一种特定的设计模式、一系列的处理单元、过滤器和处理程序,以函数的形式存在,连接在一起,形成一个异步队列,来完成对任何数据的预处理和后处理。
就是将用户从请求到响应,分步骤进行。比如检测用户是否登录、检测RBAC权限、错误捕捉
1 在express中对中间件有很多分类
/**
* 当请求进来,会从第一个中间件开始进行匹配(也就是说任何请求都会进入这个中间件)
* 如果匹配,则进来
* 如果请求进入中间件之后,没有调用 next 则代码会停在当前中间件
* 如果调用了 next 则继续向后找到第一个匹配的中间件
* 如果不匹配,则继续判断匹配下一个中间件
*/
/**
*
* 中间件本身是一个方法,该方法接收三个参数:
* Request 请求对象
* Response 响应对象
* next 下一个中间件
* 当一个请求进入一个中间件之后,如果不调用 next 则会停留在当前中间件
* 所以 next 是一个方法,用来调用下一个中间件的
*
*/
app.use(function (req, res, next) {
console.log('1')
next()
})
app.use(function (req, res, next) {
console.log('2')
next()
})
/**
* 除了以上中间件之外,还有一种最常用的
* 严格匹配请求方法和请求路径的中间件;
* 调用 next 方法也是要匹配的(不是调用紧挨着的那个)
* app.get
* app.post
* 前面的中间件可以向后面的中间件传值
*/
app.use(function (req, res, next) {
console.log(1)
req.foo='传值'
next()
})
// 匹配路径
app.use('/aa',function (req, res, next) {
//打印传值
console.log(req.foo)
next()
})
// 匹配路径和请求方式
app.get('/abc', function (req, res, next) {
console.log('abc')
next()
})
app.post('/', function (req, res, next) {
console.log('/')
next()
})
2.除此之外express还有很多其他的中间件,如**:
- express.static 静态文件处理中间件、
- express.json 解析请求参数的中间件、
- express.urlencodes url编码中间件
- 全局错误处理中间件(一般配置在所有路由之后)
app.get("/", function (req, res, next) {
fs.readFile('./a.txt', function(err,data){
if(err){
// 出现异常抛出去给全局错误处理中间件
next(err)
}
})
})
/ 错误处理中间
app.use((err, req, res, next) => {
res.status(500).send(err.message);
})
3.第三方中间件
- cookie-parse 等等
九、express 配置反向代理
express配置反向代理
什么是反向代理,简单说就是页面请求某接口,将该请求转发到另一个地址去处理。
这里我们使用http-proxy模块做代理(还有http-proxy-middleware模块)。有两种配置方式具体如下:
const express = require('express');
const httpProxy = require('http-proxy');
var app = express();
//创建代理对象
let proxy = httpProxy.createProxyServer({
//代理地址为http时
target: 'http://www.xxx.com',
//是否需要改变原始主机头为目标URL
changeOrigin: true,
//cookie的作用域
cookieDomainRewrite: {
'*': proxyUri
}
// 当地址为https时加上秘钥和
// ssl: {
// key: fs.readFileSync('server_decrypt.key', 'utf8'),
// cert: fs.readFileSync('server.crt', 'utf8')
// },
// if you want to verify the SSL Certs
// secure: false
});
//配置错误处理
proxy.on('error', function (err, request, response) {
response.writeHead(500, {
'Content-Type': 'text/plain',
});
response.status(500).end('服务器异常!');
});
//proxy使用第一种方式
/*app.use('/api', function(req, res){
//这里可以做处理
req.url = 'api' + req.url;
proxy.web(req, res);
return;
})*/
//proxy第二种配置方式
app.use('/api', proxy);
app.listen('3000', function () {
console.log(`running express`);
});
十、express配置使用cookie和路由
const express = require('express');
const cookie = require('cookie-parser');
var app = express();
//可以创建路由,路由对象和express对象差不多,有路由就可以分模块去写请求了
const routerTmp = express.Router();
//使用cookie中间件
routerTmp.use(cookie());
//运用
routerTmp.get('/setCookie', function(req,res){
//设置cookie,这里我们不设置签名
res.cookie('cookieID','123456');
res.end();
});
routerTmp.get('/getCookie', function(req,res){
//cookie的读取
console.log(req.cookies);
res.end();
}
routerTmp.get('/delCookie', function(req,res){
//删除cookie
res.clearCookie('cookieID');
res.end();
}
//再使用这个路由,并配置请求路由的前缀
app.use('/router',routerTmp);