课程目标
- 清楚后端大概的一个体系;
- 知道如果要写一个后端框架,去读
DB,显示;【补位】【卡位】
实现一个 compose
function discount(ctx, next) {
console.log('starting discount ...');
next(ctx * 0.8);
console.log('ending discount ...');
}
function num(ctx, next) {
console.log('starting num ...');
next(ctx * 10);
console.log('ending num ...');
}
function express(ctx, next) {
console.log('starting express ...');
next(ctx + 12); // 不包邮,12 运费
console.log('ending express ...');
}
// 面试官,实现这个 compose
// [ discount, num, express ]
function compose(args) {
let result;
return function (ctx) {
// ctx 初始化时是参数 150 元,要不断地计算这个值
let i = 0;
let dispatch = function (i, ctx) {
let fn;
if (i < args.length) fn = args[i]; // fn 就是每一个函数
if (i === args.length) {
result = ctx;
return;
}
return fn(ctx, dispatch.bind(null, ++i));
};
dispatch(i, ctx);
return result;
};
}
const sell = compose([num, discount, express]);
console.log(sell(150)); // 1212元
express / koa
express 和 koa 有什么区别 ?
-
概念:
-
express是一个基于node.js平台的一个灵活的web应用开发框架,connect中间件; -
koa2相对来说,更新一些,也是由express原班人马打造的框架;
-
-
集成性:
express内置了视图、static等部分;koa要通过中间件来实现;
什么是洋葱模型
const Koa = require('koa');
const app = new Koa();
// 中间件 1
app.use((ctx, next) => {
console.log('querying start 1');
next();
console.log('querying end 1');
});
// 中间件 2
app.use((ctx, next) => {
console.log('querying start 2');
next();
console.log('querying end 2');
});
// 中间件 3
app.use((ctx, next) => {
console.log('querying start 3');
next();
console.log('querying end 3');
});
app.listen(8000, () => {
console.log('Server is starting');
});
/*
querying start 1
querying start 2
querying start 3
querying end 3
querying end 2
querying end 1
*/
const Koa = require('koa');
const app = new Koa();
// 这里是异步的
const api = () =>
new Promise(resolve => {
setTimeout(() => {
console.log('timing...');
resolve(100);
}, 100);
});
app.use(async (ctx, next) => {
console.log('querying start 1');
const result = await api();
ctx.result = result;
await next();
console.log('querying end 1');
});
app.use(async (ctx, next) => {
console.log('querying start 2', ctx.result);
next();
console.log('querying end 2');
});
app.use(async (ctx, next) => {
console.log('querying start 3');
next();
console.log('querying end 3');
});
const main = ctx => {
ctx.body = 'hello world';
};
app.use(main);
app.listen(8000, () => {
console.log('Server is starting');
});
/*
querying start 1
timing...
querying start 2 100
querying start 3
querying end 3
querying end 2
querying end 1
*/
koa 框架原理解析
// ⼊⼝⽅法
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
// (res, req) => { }
callback() {
// 处理中间件,等一下看 compose 和 this.middleware
// this.middleware 是一个数组,存放的就是 app.use 的回到函数
const fn = compose(this.middleware);
// 错误处理,listenerCount是EventEmitter类的函数
if (!this.listenerCount('error')) this.on('error', this.onerror);
// 传递给createServer的就是下面这个函数
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
// 这里等到response再看
const handleResponse = () => respond(ctx);
// 给请求结束增加一个回调,这个onerror是ctx的onerror,不是app的onerror
onFinished(res, onerror);
// 等一下看这个
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
// 添加中间件⽅法
use(fn) {
...
this.middleware.push(fn);
return this;
}
createContext(req, res) {
// 每次请求,ctx都是一个新的对象
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
// 原生的req和res
context.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
// koa生成的request和response
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
koa-compose
'use strict'
/**
* Expose compositor.
*/
module.exports = compose
/**
* Compose `middleware` returning
* a fully valid middleware comprised
* of all those which are passed.
*
* @param {Array} middleware
* @return {Function}
* @api public
*/
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
koa 常用中间件
koa2-corskoa-statickoa-bodyparser
BFF
什么是 BFF
不是一种技术,而是一种在后端普遍采用微服务的情况下,作为适配层,更好的为前端服务。
BFF 一般的作用有哪些
- 优势:
- 降低沟通成本;
- 提升用户;
- 问题:
- 分工;
- 加了一层,比较繁琐;
- 资源浪费;
- 特点:
- 数据处理
- 数据聚合和裁剪;
- 格式转换;
- 流量处理
- 请求分发能力,代理,可靠性;
- 安全问题
- 鉴权;
- CSP
- 数据处理
Sequelize (gitstar 20K)
Sequelize 是一个基于 promise 的 Node.js ORM,目前支持 Postgres、MySQL、MariaDB、SQLite 以及 Microsoft SQL Server,它具有强大的事务支持,关联关系,预读和延迟加载,读取复制等功能。
其他常用的 ORM:
typeorm——TS支持比较好prisma—— 类型推导更强knexbookshelf
安装 Sequelize
npm i sequelize
# 使用 npm
npm i pg pg-hstore # PostgreSQL
npm i mysql2 # MySQL
npm i mariadb # MariaDB
npm i sqlite3 # SQLite
npm i tedious # Microsoft SQL Server
npm i ibm_db # DB2
一些常用命令,来在数据库里创建相应的数据表
根据项目下的 .sequelizerc 文件,执行
执行 sequelize init 响应操作
-
npx sequelize init:config生成config.json的文件; -
npx sequelize init:migrations生成migrations文件夹;
执行 sequlize migration
npx sequelize migration:generate --name=init-users初始化数据表;
对数据操作
-
npx sequelize db:migrate升级数据库,将表推送到数据库中; -
npx sequelize db:migrate:undo:all回退到初始状态;