1. express安装
-
全局安装express命令:
npm install express-generator -g
-
生成项目:
express 项目名
-
安装依赖:
cd 项目名
cnpm install
-
启动项目(端口默认3000):
npm start
-
安装nodemon跟cross-env(对不同环境进行监听,热监听):
cnpm i nodemon cross-env --save-dev
-
修改package.json中的配置:
"scripts": { "start": "node ./bin/www", "dev": "cross-env NODE_ENV=dev nodemon --inspect=9829 ./bin/www.js", "prod": "cross-env NODE_ENV=prod nodemon ./bin/www.js" }
-
重写启动项目:
npm run dev
2. 了解express入口app.js代码,及相关插件作用
-
app是本次访问的一个实例,每次http请求都会创建一个app实例。
-
res.json({ }):
- 将json数据以字符串格式返回到客户端,类似于req.end(“json字符串”)。
- 自动设置返回格式:res.setHeader(“Content-type”, “application/json”);
-
如何解析表单、json格式的数据:
-
express.json():解析请求头
req.headers["content-type"] == "application/json"
的请求参数,并存放到req.body中。 -
express.urlencoded():解析请求参数是表单类型的数据,并存放到req.body中。
app.use(express.json()); app.use(express.urlencoded({ extended: false }));
-
-
cookie-parser:解析cookie,并将其存放到req.cookie中
var cookieParser = require('cookie-parser'); app.use(cookieParser());
-
morgan:用来写日志
var logger = require('morgan'); app.use(logger('dev'));
-
如何创建子路由? 子路由跟父路由关系?
-
创建子路由
- 引入express
- 实例化: let router = express.Router()
- router.get(“子路由路径”, 中间件函数)
var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;
-
app.js中的父路由,跟子路由的关系
//例如:在app.js中注册user路由? //"/users"是父路由,要访问usersRouter中的路由,得拼接上当前父路由'/users'. app.use('/users', usersRouter);
-
3. express中间件、中间件函数
-
next( ):会执行下一个符合条件的函数,若不执行next( )请求就会在当前位置结束。
-
express()中几个处理路由的函数:当 请求路径 满足 处理路由函数条件 的时候就会进去执行对应的中间件函数。
-
任意请求路径都会进入的中间件函数
app.use((req, res, next) => { })
-
路由在请求路径中存在,就会进到这个中间件函数
app.use("路由", (req, res, next) => {})
-
get方式的请求,并且路由在请求路径中存在才会进到这个中间件函数
app.get("路由", (req, res, next) => { })
-
post方式的请求,并且路由在请求路径中存在才会进到这个中间件函数
app.post("路由", (req, res, next) => {})
-
4.登陆: express-session 跟 connect-redis实现将登陆信息存放到session、redis中
-
安装:
cnpm i redis --save cnpm i express-session --save cnpm i connect-redis --save
-
App.js中配置:express-session、connect-redis、设置返回客户端cookie
const session = require("express-session"); const RedisStore = require("connect-redis")(session); //连接redis相关配置 //初始化指定tokenId值的req.session空间,并将tokenId返回到客户端的cookie中 const {redisClient} = require("./db/redis"); const sessionStore = new RedisStore({ client: redisClient }); app.use(session({ resave: false, saveUninitialized: true, secret: "jiangyf0725", //自动将sessionId返回到客户端的cookie中 cookie: { path: '/', httpOnly: true, maxAge: 24 * 60 * 60 *1000 }, //redis跟session进行绑定 store: sessionStore }));
-
登陆成功接口,将用户信息存放到req.session中
//由于app.js中redis跟session进行绑定,因此设置session的同时会自动将用户信息存放到redis中 req.session.name = name; req.session.password = password;
5.登陆:登陆校验中间件
//检验中间件:创建middleware文件夹存放自定义中间件,创建检验中间件文件loginCheck.js
const {SuccessModel, ErrorModel} = require("../model/resModel");
module.exports = (req, res, next) => {
if(req.session.name) {
next();
return
}else {
res.json(
new ErrorModel("尚未登陆")
)
}
}
//在需要做校验的路由处使用
const loginCheck = rquire("./middleware/loginCheck");
app.use('/api/blog', loginCheck, blogRouter);
6. morgan写日志
-
生成环境:将日志写到指定文件中
开发环境:将日志写在控制
const env = process.env.NODE_ENV; if(env == 'dev') { //开发环境 app.use(logger('dev')); }else { //生产环境写日志文件路径 const aceessPathName = path.join(__dirname, "log", "access.log"); const writeStream = fs.createWriteStream(aceessPathName, { flag: "a" }) app.use( logger('combined', {stream: writeStream}) ); }
7. 中间件原理
-
分析:
- app.use用来注册中间件,先收集起来
- 遇到HTTP请求,根据path、method判断触发哪些
- 实现next机制,即上一个通过next触发下一个
-
面向对象实现express中间件
const http = require("http"); let slice = Array.prototype.slice; class LikeExpress { constructor() { this.routes = { use: [], get: [], post: [], } } register(path) { let infos = {}; if (typeof path == 'string') { infos.path = path; infos.stack = slice.call(arguments, 1); } else { infos.path = "/"; infos.stack = slice.call(arguments, 0); } return infos; } use() { const info = this.register.apply(this, arguments); this.routes.use.push(info); } get() { const info = this.register.apply(this, arguments); this.routes.get.push(info); } post() { const info = this.register.apply(this, arguments); this.routes.post.push(info); } mathPath(method, url) { //存放路径匹配上的路由的中间件函数 let stack = []; //根据method,拿到满足method条件的中间件函数 let arr = this.routes.use; arr.concat(this.routes[method]); //循环所有中间件函数,将满足url条件的存放到stack arr.map(item => { if (item.path.indexOf(url) == 0) { stack.push(item.stack); } }) return stack; } nextHandle(req, res, stacks) { let next = () => { //拿到第一个匹配的中间件 let middleware = stacks.shift(); if (middleware) { //执行中间件 middleware(req, res, next); } } next() } callback(req, res) { const method = req.method; const url = req.url.toLocaleLowerCase(); let stacks = this.matchPath(method, url); //实现next功能 this.nextHandle(req, res, stacks); } listen(...arg) { const server = http.createServer(this.callback); server.listen(...arg); } }