
Express 是 NodeJS 的一个 Web 框架。它通过为 Node 的 req 和 res 对象添加有用的方法和属性来增强其功能
const express = require('express');
const app = express();
const asyncActivity = async () => {
return {
success: true,
}
}
const asyncWrapper = fn => (...args) => fn(...args).catch(args[2]);
app.get('/', asyncWrapper(async (req, res, next) => {
const result = await asyncActivity();
res.json(result);
}));
app.use(function errorMiddleware(error, req, res, nexr) {
res.status(500)
res.send(error)
})Express的中间件是基于回调的
├─ bin 数据库初始化脚本
│ ├─ db 项目数据库安装,其中 setup.sh 为命令入口,初始化数据库
├─ common 共用方法、常量目录
├─ conf 数据库基本配置目录
├─ dao 代码主逻辑目录
├─ log 日志目录
├─ public 静态文件目录
├─ routes url 路由配置
├─ .eslintrc.js eslint 配置
├─ app.js express 入口
├─ package.json 项目依赖包配置
├─ README.md 项目说明
每次修改 js 文件,我们都需要重启服务器,这样修改的内容才会生效,但是每次重启比较麻烦,影响开发效果
在开发环境中引入 nodemon 插件,实现实时热更新,自动重启项目
npm install nodemon -Spackage.json中新增dev环境脚本
{
"scripts": {
"dev": "nodemon app.js"
}
}可以使用CORS(跨源资源共享)中间件来配置跨域
const express = require('express')
const cors = require('cors')
const app = express()
// 启用所有 CORS 请求
app.use(cors())
// 只允许特定的源进行跨域请求
app.use(cors({
origin: 'http://example.com',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true
}))// 单个路由配置CORS请求
const express = require('express')
const cors = require('cors')
const app = express()
// 单个路由配置CORS请求
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a Single Route'})
})
// 单个路由配置CORS请求加配置
const corsOptions = {
origin: 'http://example.com',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true
}
app.get('/goods/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a Single Route'})
})设置res.headers
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', '*')
res.header('Access-Control-Allow-Methods', '*')
next()
})morgan 是 express 默认的日志中间件,也可以脱离 express,作为 node.js 的日志组件单独使用
const express = require('express')
const morgan = require('morgan')
const fs = require('fs')
const app = express()
// 使用'morgan'中间件,并设置日志格式为'dev'
app.use(morgan('dev'))
// 输出日志到目录
const accessLogStream = fs.createWriteStream(path.join(__dirname, '/log/request.log'), { flags: 'a', encoding: 'utf8' });
app.use(morgan('combined', { stream: accessLogStream }));由于 morgan 只能记录 http 请求的日志,所以我们还需要 winston 来记录其它想记录的日志,例如:访问数据库出错等
const { format, createLogger, transports } = require('winston')
const { combine, timestamp, printf } = format
const DailyRotateFile = require('winston-daily-rotate-file') // 日志清除插件
const path = require('path')
const logFormat = printf(({ level, message, timestamp }) => {
return `${timestamp} ${level}: ${message}`
})
const logger = createLogger({
level: 'error',
format: combine(
timestamp(),
logFormat,
),
transports: [
new (transports.Console)(),
new (transports.File)({
filename: path.join(__dirname, '../logs/error.log'),
level: 'error',
}),
new (transports.File)({
filename: path.join(__dirname, '../logs/info.log'),
level: 'info',
}),
new DailyRotateFile({
filename: path.join('logs', 'app-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '300m',
maxFiles: '10d',
utc: true
})
]
})
module.exports = logger
项目中按模块划分请求处理,我们在 routes 目录下分别新建每个模块路由配置,例如用户模块,则为 user.js 文件,我们在 app.js 主入口文件中引入每个模块的路由,以用户模块进行举例,相关代码逻辑如下
// 引入用户模块路由配置
const usersRouter = require('./routes/users')
// 使用用户模块的路由配置
app.use('/users', usersRouter)其中 routes/users.js 文件中的路由配置如下:
const express = require('express')
const router = express.Router()
const logger = require('../common/logger')
// 新增用户
router.post('/addUser', (req, res, next) => {
})
// 用户列表
router.get('/queryUserList', (req, res, next) => {
})
module.exports = router
客户端通过 /users 开头的请求路径,都会跳转到用户模块路由中进行处理,比如请求 /users/addUser 会跳转到用户模块的 addUser 方法中进行处理
express-jwt 是 node.js 的一个中间件,他来验证指定 http 请求的 JsonWebTokens 的有效性,如果有效就将jsonWebTokens 的值设置到 req.user 里面,然后路由到相应的 router
const { expressjwt: expressJWT } = require('express-jwt')
app.use(expressJWT({
secret: 'my-secret',
algorithms: ['HS256'],
}).unless({
// 除了以下这些 URL,其他的URL都需要验证
path: [
'/getToken',
'/getToken/adminLogin',
'/appVersion/upload',
]
}))我们可以选择在生成 token 时,将对应登录的用户 id 注入 token 中,如文件 dao/tokenDao.js 文件中所配置的
// uid 注入 token 中
ret = {
code: 0,
data: {
token: jsonWebToken.sign({
uid: obj.uid
},
CONSTANT.SECRET_KEY, {
expiresIn: 60 * 60 * 24
}),
}
};后续我们可以在请求中,通过请求信息 req.body.uid,获取得到先前注入 token 里面的用户 id 信息。
app.use('/', express.static(path.join(__dirname, 'public')))
app.use('/public', express.static(path.join(__dirname, 'public')))安装eslint相关依赖,根目录下创建.eslintrc.js文件
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true
},
extends: [
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
'comma-dangle': 'off',
'no-unused-vars': 'off',
semi: 'off'
}
}