环境
安装 express
npm install express-generator -g
express express-test
添加 dev 环境
npm install nodemon cross-env --save-dev
package.json
{
"scripts":{
"dev":"cross-env NODE_ENV=dev nodemon ./bin/www"
}
}
中间件
const express = require("express");
const app = express();
app.use((req, res, next) => {
console.log("处理中间件");
next();
});
app.use("/api", (req, res, next) => {
console.log("处理 /api 路由");
next();
});
app.get("/api", (req, res, next) => {
console.log("处理get /api 路由");
next();
});
app.post("/api", (req, res, next) => {
console.log("处理post /api 路由");
next();
});
app.get("/api/a", (req, res, next) => {
console.log("处理get /api/a");
});
app.post("/api/b", (req, res, next) => {
console.log("处理post /api/b");
});
app.use((req, res, next) => {
console.log("处理 404");
});
app.listen(3000, () => {
console.log("server is running on port 3000");
});
原理
- app.use 用来注册中间件,先收集起来
- 遇到 http 请求,根据 path 和 method 判断触发哪些
- 实现 next 机制,即上一个通过 next 触发下一个
实现
const http = require("http");
const slice = Array.prototype.slice;
class LikeExpress {
constructor() {
// 存放中间件的列表
this.routes = {
all: [], // app.use(...)
get: [], // app.get(...)
post: [], // app.post(...)
};
}
register(path) {
const info = {};
if (typeof path === "string") {
info.path = path;
// 从第二个参数开始,转换为数组存入 stack
info.stack = slice.call(arguments, 1);
} else {
info.path = "/";
// 从第一个参数开始,转换为数组存入 stack
info.stack = slice.call(arguments, 0);
}
return info;
}
use() {
const info = this.register.apply(this, arguments);
this.routes.all.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);
}
match(method, url) {
let stack = [];
if (url === "/favicon.ico") {
return stack;
}
// 获取routes
let curRoutes = [];
curRoutes = curRoutes.concat(this.routes.all);
curRoutes = curRoutes.concat(this.routes[method]);
curRoutes.forEach((routeInfo) => {
if (url.indexOf(routeInfo.path) === 0) {
stack = stack.concat(routeInfo.stack);
}
});
return stack;
}
// 核心的next机制
handle(req, res, stack) {
const next = () => {
// 拿到第一个匹配的中间件
const middleware = stack.shift();
if (middleware) {
// 执行中间件函数
middleware(req, res, next);
}
};
next();
}
callback() {
return (req, res) => {
res.json = (data) => {
res.setHeader("Content-type", "application/json");
res.end(JSON.stringify(data));
};
const url = req.url;
const method = req.method.toLowerCase();
const resultList = this.match(method, url);
this.handle(req, res, resultList);
};
}
listen(...args) {
const server = http.createServer(this.callback());
server.listen(...args);
}
}
// 工厂函数
module.exports = () => {
return new LikeExpress();
};
session
npm install express-session --save
app.js
const session = require("express-session");
app.use(
session({
secret: "abcdefg",
cookie: {
// path: '/', // 默认配置
// httpOnly: true, // 默认配置
maxAge: 24 * 60 * 60 * 1000,
},
})
);
redis
npm install redis connect-redis --save
app.js
const redis = require('redis')
const redisClient = redis.createClient(6379, 127.0.0.1) // 端口号,IP
const session = require('express-session')
const RedisStore = require('connect-redis')(session)
const sessionStore = new RedisStore({
client: redisClient
})
app.use(session({
secret: 'abcdefg',
cookie: {
// path: '/', // 默认配置
// httpOnly: true, // 默认配置
maxAge: 24 * 60 * 60 * 1000
},
store: sessionStore
}))
logs
app.js
const fs = require("fs");
const path = require("path");
const logger = require("morgan");
const ENV = process.env.NODE_ENV;
if (ENV !== "production") {
// 开发环境
app.use(logger("dev"));
} else {
// 生产环境
const logFileName = path.join(__dirname, "logs", "access.log");
const writeStream = fs.createWriteStream(logFileName, {
flags: "a",
});
app.use(
logger("combined", {
stream: writeStream,
})
);
}