项目树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| TestDemo ├─ db // 连接数据库 │ └─ index.js ├─ router // 路由规则 │ ├─ artcata.js │ ├─ article.js │ ├─ user.js │ └─ userinfo.js ├─ router_handler // 路由函数体 │ ├─ artcata_handler.js │ ├─ article_handler.js │ ├─ userinfo_handler.js │ └─ user_handler.js ├─ schema //数据校验规则 │ ├─ article.js │ └─ user.js ├─ uploads // 文件上传存储位置 ├─ app.js // 程序启动入口 ├─ config.js // 全局变量配置 ├─ package-lock.json └─ package.json
|
程序入口app.js
跨域问题
在写项目是一般要都会遇到跨域的问题,要如何解决呢?
在客户端对服务端请求的数据的程序入口添加cors
中间件来解决跨域问题
app.js1 2 3 4
| const cors = require('cors')
app.use(cors())
|
表单数据解析
只需要添加urlencoded中间件
就可以解析表单数据了
app.js1 2 3
| const express = require('express')
app.use(express.urlencoded({ extended: false }))
|
错误中间件
服务端在处理数据时难免会有错误,这时需要一个中间件来处理这些错误。
先封装一个中间件用来处理发生错误时给客户端返回的响应数据,可以减少代码冗余。
在给客户端返回数据时应该有规定的格式,应该有响应码和响应描述{ status:0 , msg: '响应内容' }
app.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| app.use(function (req, res, next) { res.cc = function (err, status = 1) { res.send({ status, msg: err instanceof Error ? err.message : err, }) } next() })
app.use(function (err, req, res, next) { if (err instanceof joi.ValidationError) return res.cc(err)
if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
res.cc(err) })
|
登录 & 注册
解析数据
利用urlencoded
中间件解析表单传过来的数据。
导入登录注册的路由并定义入口地址前缀/api
app.js1 2 3 4 5 6
| app.use(express.urlencoded({ extended: false }))
const userRouter = require('./router/user') app.use('/api',userRouter)
|
登录
请求地址:127.0.0.1:3000/api/login
参数格式:
username
:value
password
:value
注册
请求地址:127.0.0.1:3000/api/reguser
参数格式:
username
:value
password
:value
在注册和登录要进行数据的校验:
- 先验证数据是否为空或者数据是否合乎规则
- 查询用户名是否被占用
- 登录成功/添加成功
在验证用户名和密码一般需要使用joi
模块来校验规则。
以下是schema/user.js
的文件,主要功能是对客户端对个人用户信息修改进行规则验证。
schema/user.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| const joi = require('joi')
const username = joi.string().alphanum().min(1).max(15).required()
const password = joi .string() .pattern(/^[\S]{6,12}$/) .required()
const id = joi.number().required().integer().min(1)
const nickname = joi.string().required()
const email = joi.string().email().required()
const avatar = joi.string().dataUri().required()
exports.reg_login_schema = { body: { username, password, }, }
exports.update_userinfo_schema = { body:{ id, nickname, email, }, }
exports.update_password_schema = { body: { oldPwd: password, newPwd: joi.not(joi.ref('oldPwd')).concat(password), }, }
exports.reg_avatar_schema = { body:{ avatar, }, }
|
然后在请求时校验规则,在导入规则对象时记得要加{}
,以下是router/user.js
文件,主要功能是对个人信息相关的路由进行控制。
router/user.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const express = require('express'),
const router = express.Router()
const user_handler = require('../router_handler/user_handler')
const expressJoi = require('@escook/express-joi')
const { reg_login_schema } = require('../schema/user')
router.post('/reguser',expressJoi(reg_login_schema),user_handler.reguser )
router.post('/login',expressJoi(reg_login_schema), user_handler.login)
module.exports = router
|
请求地址处理
在设计项目初期,应该给不同类型的内容设计不同的请求地址,防止项目路由混乱。
-
比如在注册登录使用路由前缀/api
-
在用户发布文章使用前缀/my/article
-
在用户查阅个人信息时使用前缀/my
路由处理
项目有非常多的请求地址,应该有条理的把路由整齐划分,将路由请求方式和路由函数体分开,以便能够查阅清洗条例的路由请求方式。
在路由请求方式中,记得在末尾导出路由对象module.exports = router
。
在路由函数体也要导出函数对象exports.login = (req, res) => {}
比如以下:
1 2 3 4 5 6 7 8 9 10 11
| TestDemo ├─ router // 路由规则 │ ├─ artcata.js │ ├─ article.js │ ├─ user.js │ └─ userinfo.js ├─ router_handler // 路由函数体 │ ├─ artcata_handler.js │ ├─ article_handler.js │ ├─ userinfo_handler.js │ └─ user_handler.js
|
数据校验
在客户端给服务通过url
传参数的时候,
在joi模块
校验应该把规则变量挂载到params
而不是body
下,使用id
的时候是通过req.params.id
1 2 3 4 5 6
| exports.delete_cate_schema = { params:{ id, } }
|
此项目所使用到的模块以及某些案例
数据校验案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| const express = require('express') const app = express()
const Joi = require('joi')
const expressJoi = require('@escook/express-joi')
app.use(express.urlencoded({ extended: false }))
const userSchema = { body: { username: Joi.string().alphanum().min(3).max(12).required(), password: Joi.string() .pattern(/^[\S]{6,15}$/) .required(), repassword: Joi.ref('password') }, query: { name: Joi.string().alphanum().min(3).required(), age: Joi.number().integer().min(1).max(100).required() }, params: { id: Joi.number().integer().min(0).required() } }
app.post('/add', expressJoi(userSchema), function (req, res) { const body = req.body res.send(body) })
app.use(function (err, req, res, next) { if (err instanceof Joi.ValidationError) { return res.send({ status: 1, message: err.message }) } res.send({ status: 1, message: err.message }) })
app.listen(3001, function () { console.log('Express server running at http://127.0.0.1:3001') })
|
所用到的包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| { "name": "TestDemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@escook/express-joi": "^1.1.1", "bcryptjs": "^2.4.3", "cors": "^2.8.5", "express": "^4.17.1", "express-jwt": "^5.3.3", "joi": "^17.6.0", "jsonwebtoken": "^8.5.1", "multer": "^1.4.2", "mysql": "^2.18.1" } }
|
删除文章案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| exports.deleteCateById = (req,res)=>{ const sql = `select * from ev_article_cate where id =?` db.query(sql,req.params.id,(err,results)=>{ if (err) return res.cc(err) const delSql = `update ev_article_cate set is_delete = 1 where id = ?` db.query(delSql,req.params.id,(err,results)=>{ if (err) return res.cc(err) if (results.affectedRows !== 1) return res.cc('删除文章失败') res.cc('删除文章成功',0) }) }) }
|