一、创建项目
二、目录结构
三、配置常用中间件
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: ()=>{ } ,为该字段添加校验。