node.js养成计划(九)express提高篇

一、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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值