如何在vue项目中用Express做中间层
2022-10-24
6 min read
背景:在已有的vue2项目中,需要进行BFF开发,进行数据聚合和接口转发。为便于代码管理,将Express项目和前端放在一个代码仓库内,不进行拆分。
一、在项目根目录下新建server目录,并进入到server目录内
二、初始化项目
1.初始化
npm init
为了避免依赖会有版本冲突,Express单独维护自己的node_modules
2.安装express
npm i express --save
3.安装typescript
安装typescript、ts-node(用来监听ts文件的变化并更新服务),node和express的声明文件@types/node @types/express
npm i typescript ts-node @types/express @types/node -D
4. ts环境配置
新建tsconfig.json文件
{
// 指定需要编译文件 否则默认当前目录下除了exclude之外的所有.ts, .d.ts,.tsx 文件
"include":["app.ts"],
"compilerOptions": {
// 指定使用的模块 :commonjs amd system cmd or es2015
"module": "commonjs",
/* 注意:如果未指定--lib,则会注入默认的librares列表。注入的默认库为:
对于 --target ES5: DOM,ES5,ScriptHost
对于 --target ES6: DOM,ES6,DOM.Iterable,ScriptHost
TS 绝不会在您的代码中注入polyfill,所以需要你自己制定编译lib */
"lib": ["ESNext"],
// 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016','ES2017', or 'ESNEXT'
"target": "ES6",
// 编译输入文件存放的目录
"outDir": "build",
// 是否生成.map文件
"sourceMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
/* Strict Type-Checking Options */
// 严格模式将会打开下面的几个选项
"strict": false,
/* 不允许变量或函数参数具有隐式any类型,例如
function(name) {
return name;
} */
"noImplicitAny": true,
// null类型检测,const teacher: string = null;会报错
"strictNullChecks": true,
// 对函数参数进行严格逆变比较
"strictFunctionTypes": true,
// 严格检查bind call apply
"strictBindCallApply": true,
// 此规则将验证构造函数内部初始化前后已定义的属性。
"strictPropertyInitialization": true,
// 检测this是否隐式指定
"noImplicitThis": true,
// 使用js的严格模式,在每一个文件上部声明 use strict
"alwaysStrict": true,
/* Additional Checks */
// 默认false,是否检测定义了但是没使用的变量
"noUnusedLocals": true,
// 用于检查是否有在函数体中没有使用的参数
"noUnusedParameters": true,
// 用于检查函数是否有返回值,设为true后,如果函数没有返回值则会提示
"noImplicitReturns": true,
// 用于检查switch中是否有case没有使用break跳出switch
"noFallthroughCasesInSwitch": true,
}
}
5. 新建controller目录,用来管理路由
创建article.ts文件,定义文章相关的路由
import express from 'express'
import {Response, Request} from 'express'
const router = express.Router()
// 文章分类列表
// 路由处理函数一般会有读写数据库的操作,一般会抽离出来,单独在service目录下管理
router.get('/cates/list', function(req:Request, res:Response)=>{
// do something
res.send({
code: 0,
msg: 'success',
data:[]
})
})
export default router
6. server目录下新建入口文件 app.ts
import express from 'express'
import articleRouter from './controller/artcate''
import mw from './middleware'
const app = express()
app.use('/article', articleRouter)
// 错误级别中间件
app.use(mw.errorHandler)
app.listen(3007, () => {
console.log('api server is running at http://127.0.0.1:3007')
})
7.package.json中新增启动脚本
"script":{
"dev": "nodemon app.ts"
}
此时运行npm run dev
,在postman中就可以正常请求/article/cates/list 接口了
三、公共配置
1. 日志输出
安装pino和dayjs
npm i pino pino-pretty dayjs --save
npm i @types/pino -D
异常监控 Sentry
2. 统一响应参数
新建const目录,用于存放常量。在const目录下新建code.ts用于存放相应参数,包括status、code和message
export enum HTTP_CODE {
SUCCESS = 0,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
NOT_FOUND = 404,
BAD_SERVER = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
UNAVAILABLE = 503
}
export enum CODE_MESSAGE {
SUCCESS = 'success',
BAD_REQUEST = 'BAD_REQUEST',
UNAUTHORIZED = '未授权',
NOT_FOUND = '404',
BAD_SERVER = '请求异常',
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
BAD_GATEWAY = 'BAD_GATEWAY',
UNAVAILABLE = 'UNAVAILABLE'
}
// 状态类型 只能是Code中所枚举的状态
export type CODE_TYPE = keyof typeof HTTP_CODE
utils目录下新建commonRes.ts ,对响应进行统一处理
import { Console } from 'console'
import { Response } from 'express'
import { HTTP_CODE, CODE_MESSAGE, CODE_TYPE } from '../common/const/code'
import logger from './logger'
interface ResOption {
type?: CODE_TYPE
message?: string
status?: number
}
// 默认成功响应
function commonRes(res: Response, data: any, options?: ResOption) {
options = Object.assign({ type: HTTP_CODE[0] }, options || {}) // 默认success
const { type, status, message } = options
let resStatus = status
if (resStatus === undefined) {
// 根据状态设置状态码
resStatus = type === HTTP_CODE[3000] ? 200 : 409
}
// 响应参数
const sendRes: { code: number; data: any; message?: string } = {
code: HTTP_CODE[type as CODE_TYPE],
data
}
// 响应描述
message && (sendRes.message = message)
return res.status(resStatus).send(sendRes)
}
// 错误响应
commonRes.error = function (
res: Response,
data: unknown,
status: number,
message?: unknown
) {
const type = HTTP_CODE[status]
const msg = message || (CODE_MESSAGE[type] as string)
logger.error(msg)
this(res, data, {
type: type,
message: msg,
status: status
})
}
// 无权限响应
commonRes.denied = function (res: Response, data: unknown) {
this(res, data, {
type: 'UNAUTHORIZED',
message: CODE_MESSAGE['UNAUTHORIZED'],
status: 401
})
}
export default commonRes