一、创建项目
二、目录结构
三、配置常用中间件
3.1 解析请求体
- express.json()
- express.urlencoded()
3.2 日志输出
- morgan()
3.3 为客户端提供跨域资源请求
- cors()
四、路由设计
本项目的接口设计参考:https://github.com/gothinkster/realworld/tree/master/api
router/index.js:
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {
res.send('Hello World')
})
// 用户相关的路由
router.use(require('./user.js'))
// 用户资料相关路由
router.use(require('./profile.js'))
// 文章相关的路由
router.use(require('./article.js'))
// 标签相关的路由
router.use(require('./tag.js'))
module.exports = router
router/user.js:
const express = require('express')
const userController = require('../controller/user.js')
const router = express.Router()
// 用户登录
router.post('/users/login', userController.login)
// 用户注册
router.post('/users', userController.register)
// 获取当前登录用户
router.get('/user', userController.getCurrentUser)
// 更新当前登录的用户资料
router.put('/user', userController.updateCurrentUser)
module.exports = router
router/article.js:
const express = require('express')
const articleController = require('../controller/article.js')
const router = express.Router()
// 获取文章列表
router.get('/articles', articleController.getArticles)
// 获取用户关注的作者的文章列表
router.get('/articles/feed', articleController.getFeedArticles)
// 获取文章
router.get('/articles/:slug', articleController.getArticle)
// 创建文章
router.post('/articles',articleController.createArticle)
// 更新文章
router.put('/articles/:slug', articleController.updateArticle)
// 删除文章
router.delete('/articles/:slug', articleController.deleteArticle)
// 为文章添加评论
router.post('/articles/:slug/comments', articleController.addComments)
// 从文章中获取评论
router.get('/articles/:slug/comments', articleController.getComments)
// 删除评论
router.delete('/articles/:slug/comments/:id', articleController.deleteComments)
// 收藏文章
router.post('/articles/:slug/favorite', articleController.favoriteArticle)
// 取消收藏文章
router.delete('/articles/:slug/favorite', articleController.unFavoriteArticle)
module.exports = router
router/profile.js:
const express = require('express')
const profileController = require('../controller/profile.js')
const router = express.Router()
// 获取用户个人资料
router.get('/profiles/:username', profileController.getProfile)
// 关注用户
router.post('/profiles/:username/follow', profileController.followUser)
// 取消关注用户
router.delete('/profiles/:username/follow', profileController.unFollowUser)
module.exports = router
router/tag.js:
const express = require('express')
const tagsController = require('../controller/tag.js')
const router = express.Router()
// 获取文章标签
router.get('/tags', tagsController.getTags)
module.exports = router
五、提取控制器模块
controller/user.js:
const {
User } = require('../model/index.js')
// 用户登录
exports.login = async (req, res, next) => {
try {
// 处理请求
res.send('post /users/login')
} catch (err) {
next(err)
}
}
// 用户注册
exports.register = async (req, res, next) => {
try {
// 1. 获取请求体数据
console.log(req.body)
// 2. 数据验证
// 2.1 基本数据验证
// 2.2 业务数据验证
// 3. 验证通过,将数据保存到数据库
const user = new User(req.body.user) // 构造user对象
await user.save() // 保存到数据库
// 4. 发送成功响应
res.status(201).json({
user
})
} catch (err) {
next(err)
}
}
// 获取当前登录用户
exports.getCurrentUser = async (req, res, next) => {
try {
// 处理请求
res.send('get /user')
} catch (err) {
next(err)
}
}
// 更新当前登录的用户资料
exports.updateCurrentUser = async (req, res, next) => {
try {
// 处理请求
res.send('put /user')
} catch (err) {
next(err)
}
}
controller/tag.js:
// 获取文章标签
exports.getTags = async (req, res, next) => {
try {
// 处理请求
res.send('get /tags 获取文章标签')
} catch (err) {
next(err)
}
}
controller/article.js:
// 获取文章列表
exports.getArticles = async (req, res, next) => {
try {
// 处理请求
res.send('get /articles')
} catch (err) {
next(err)
}
}
// 获取用户关注的作者的文章列表
exports.getFeedArticles = async (req, res, next) => {
try {
// 处理请求
res.send('get /articles/feed')
} catch (err) {
next(err)
}
}
// 获取文章
exports.getArticle = async (req, res, next) => {
try {
// 处理请求
res.send('get /articles/:slug 获取文章')
} catch (err) {
next(err)
}
}
// 创建文章
exports.createArticle = async (req, res, next) => {
try {
// 处理请求
res.send('post /articles 创建文章')
} catch (err) {
next(err)
}
}
// 更新文章
exports.updateArticle = async (req, res, next) => {
try {
// 处理请求
res.send('put /articles/:slug 更新文章')
} catch (err) {
next(err)
}
}
// 删除文章
exports.deleteArticle = async (req, res, next) => {
try {
// 处理请求
res.send('delete /articles/:slug 删除文章')
} catch (err) {
next(err)
}
}
// 为文章添加评论
exports.addComments = async (req, res, next) => {
try {
// 处理请求
res.send('post /articles/:slug/comments 为文章添加评论')
} catch (err) {
next(err)
}
}
// 从文章中获取评论
exports.getComments = async (req, res, next) => {
try {
// 处理请求
res.send('get /articles/:slug/comments 从文章中获取评论')
} catch (err) {
next(err)
}
}
// 删除评论
exports.deleteComments = async (req, res, next) => {
try {
// 处理请求
res.send('delete /articles/:slug/comments/:id 删除评论')
} catch (err) {
next(err)
}
}
// 收藏文章
exports.favoriteArticle = async (req, res, next) => {
try {
// 处理请求
res.send('post /articles/:slug/favorite 收藏文章')
} catch (err) {
next(err)
}
}
// 取消收藏文章
exports.unFavoriteArticle = async (req, res, next) => {
try {
// 处理请求
res.send('delete /articles/:slug/favorite 取消收藏文章')
} catch (err) {
next(err)
}
}
controller/profile.js:
// 获取用户个人资料
exports.getProfile = async (req, res, next) => {
try {
// 处理请求
res.send('get /profiles/:username')
} catch (err) {
next(err)
}
}
// 关注用户
exports.followUser = async (req, res, next) => {
try {
// 处理请求
res.send('post /profiles/:username/follow')
} catch (err) {
next(err)
}
}
// 取消关注用户
exports.unFollowUser = async (req, res, next) => {
try {
// 处理请求
res.send('delete /profiles/:username/follow')
} catch (err) {
next(err)
}
}
六、配置错误统一处理中间件
七、用户注册 将数据保存到数据库 && 提取通用数据模型
model/index.js:
model/user.js:
model/article.js:
model/base-model.js:
八、数据验证
express-validator官方文档
安装:npm install express-validator
九、提取验证中间件模块
十、用户注册的密码加密处理
nodejs内置模块 crypto 模块
提供了加密功能,实现了包括对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装。
- 查看 crypto 模块支持的 hash 函数:crypto.getHashes()
在模型的password字段上进行md5加密:
关于Model模型中字段的一些属性:
-
type:字段的类型;
-
require:true或false,指定该字段是否必填(默认false,不是必填的);
-
max与min:数字,只能用在Number类型,可以表示最大值与最小值是多少;
-
maxlength与minlength:只能用于String字符串类型,表示最长或最短字符串长度;
-
enum: [‘男’, ‘女’],可枚举类型,后边是一个数组,也就是值必须在枚举值中,并且枚举类型必须是String;
例:sex: { type: String, enum: [ ‘男’, ‘女’ ] } sex字段的值必须在枚举值中,且必须为字符串。
-
defalt: ‘xxx’, 默认值,如果没有传参数时,会取默认值,存入数据库。
-
trim: true, 去除字符串两端空格(保存前对字段进行 .trim());
-
uppercase:true , 只能用于字符串,表示把所有小写字母转为大写字母(保存前对字段进行.toUpperCase());
-
lowercase:true , 只能用于字符串,表示把所有大写字母转为小写字母(保存前对字段进行.toLowerCase());
例:test{ type: String, lowercase: true }。
-
select: true/false,指定该字段在查询出的数据中是否显示,true表示查询数据过滤该字段;
-
get: () => {} 和set:() => {},两个函数使用Object.defineProperty( ) 来定义getter、setter。
-
validate: ()=>{ } ,为该字段添加校验。