微博用户管理(注册)

功能列表

  • 用户管理(登录和注册)
  • 用户设置(修改基本信息,修改密码,退出登录)
  • 创建微博,暂不显示微博列表
  • 个人主页,显示个人微博列表和个人信息,暂不做关注功能
  • 广场页(使用缓存)
  • 关注和取消关注,显示粉丝和关注人
  • 首页,显示我的微博和我关注的人的微博
  • @和回复
  • @提到我的

用户管理

  1. 页面:模板和路由
  2. 数据建模
  3. 开发注册功能
  4. 开发登录功能
  5. 抽离loginCheck中间件
  6. 单元测试

注册

创建页面:模板和路由

回顾技术方案

注册
注册页直接通过一个api来判断用户名是否已存在,不需要点击注册后才通过请求从后端获取来判断是否重复,提高用户体验
登录
统一的header和footer:通过一些参数来判断是否显示导航
统一的header和footer

步骤

  1. 查看缓存是否清空git status
  2. 增加项目分支git checkout -b feature-logingit bush
    • 增加的项目分支是新功能则git checkout -b feature-xxx修改bug则git checkout -b fix-xxx
  3. 创建2个模板,src-views下新建register.ejs、login.ejs
  4. 创建路由,routes-views下新建user.js创建路由
  5. 注册路由,app.js中引入并注册路由app.js
  6. 运行程序,访问登录、注册页:登录、注册页

数据建模

回顾技术方案

users表
这里先只用到users表(DECIMAL是非常小的数据类型,很适合性别)

步骤

  1. src-db下新建types.js封装sequelize的数据类型,这样在建表时就不需要每次都引一下sequelize了:types.js
  2. src-db下新建model文件夹-新建User.js 创建用户数据模型(users表)User.js补充:unique:true规定“数据唯一”,也就是说无法传入同样的2个数据到这一列。defaultValue规定“默认值”,即这一项什么也没传入数据表中时的默认数据
  3. src-db-model文件夹下新建index.js 数据模型入口文件,也就是做统一输出:index.js
  4. 执行sequelize同步数据库时,src-db-sync.js中,注意引入需要同步的数据模型sync.js
  5. 测试:手动删除原先的users表(users表中有blogs的外键,所以需要先删除blogs表),执行node src/db/sync.js将sequelize同步数据库测试users表

开发注册功能(注册接口)

回顾技术方案

整体架构设计

  • routes层:根据路由,将相应的数据(req)传给controller层
  • controller层:获取routes层传递的数据,调用service层的方法处理业务逻辑,并返回统一处理格式的数据
  • service层:创建 增删查改的方法,并定义数据表的某些默认数据
  • db层:连接数据库,定义模型(创建表)

路由和分层

  1. 【routes层】创建注册页需要的路由:src-routes-api下新建user.js,创建2个src-views-register.ejs中需要用到的接口:user.js
  2. 注册路由,app.js中app.js
  3. 【controller层】书写业务逻辑并统一返回格式:src下新建controller文件夹-新建user.jsuser.js
  4. 【services层】处理数据:src下新建services文件夹-新建user.jsuser.js(关于**Object.assign()合并对象**可参考笔记https://huanglizhu.github.io/2019/11/13/ES6%E6%89%A9%E5%B1%95%20%E5%AF%B9%E8%B1%A1%E6%89%A9%E5%B1%95/#Object-assign-合并对象-类似-扩展运算符)

格式化用户信息

  1. conf下新建constant.js 存放所以常量constant.js
  2. 【services层】创建格式化用户信息的formatUser方法:src-services文件夹下新建_format.js_format.js(注意**区分typeof和instanceof运算符**)
  3. 【services层】使用formatUser方法 格式化数据: src-services-user.js:src-services-user.js

完成isExist(判断用户名是否已存在)

  1. 创建统一格式的模型: src下新建model文件夹-新建ResModel.js 用于统一返回格式:ResModel.js大写字母开头的js文件一般表示 返回一个class
  2. 统一管理错误信息:model文件夹下新建ErrorInfo.jsErrorInfo.js
  3. 【controller层】判断用户名是否已存在,并使用模型统一返回格式: src-controller-user.js中,根据services层返回的数据判断用户名是否已存在:controller-user.js
  4. 【路由层】判断用户名是否已存在: src-routes-api-user.js中,引入isExist,判断用户名是否已存在:src-routes-api-user.js
  5. 测试:运行程序,进入注册页,输入用户名:测试

完成register

  1. 【路由层】将注册相关数据传给controller层在src-routes-api-users.js中,通过解析前端post请求中所带数据解析前端post请求中所带数据调用controller层的register方法进行注册调用controller层的方法进行注册
  2. 【controller层】接受路有层传递的数据,使用services层的createUser方法来创建用户image.png使用对象的解构赋值可以传入乱序的函数参数
  3. 【services层】新建在users表中创建用户的createUser方法:src-services-user.js
  4. 测试:注册了1次zhangsan后无法再次进行注册:注册了1次zhangsan后

密码加密

  1. 统一管理密钥常量: conf文件夹下新建secretKeys.js,统一管理session和用户密码的密钥常量:secretKeys.js
  2. 修改session密钥:app.js中给session配置的密钥换成从secretKeys.js中获取:app.js
  3. 创建加密方法: utils文件夹下新建cryp.js
    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
    /**
    * @description 加密方法
    * @author hlz
    */

    const crypto = require('crypto')
    const { CRYPTO_SECRET_KEY } = require('../conf/secretKeys')

    /**
    * md5 加密(hex:16进制)
    * @param {string} content 明文
    */
    function _md5(content) {
    const md5 = crypto.createHash('md5')
    return md5.update(content).digest('hex')
    }

    /**
    * 加密方法
    * @param {string} content 明文
    */
    function doCrypto(content) {
    const str = `password=${content}&key=${CRYPTO_SECRET_KEY}`
    return _md5(str)
    }

    module.exports = doCrypto
    (可参考crypto库 md5加密
  4. 【controller层】引入并使用cryp.js中的doCrypto方法对密码加密:
    1
    const doCrypto = require('../utils/cryp')
    controller-user.js
  5. 测试:注册lisi,输入密码为123,存储到数据库的是加密后的16进制密码:测试

schema格式校验

  • 在routes层对页面传来的数据进行格式校验,比如微博的长度、登录校验等
  • 在这我们使用json schema设置格式校验的校验规则
  • 当数据从页面传到routes层时,我们就进行数据的格式校验,不符合规则的数据直接不往后传递,不再等到数据库才判断格式是否符合要求,提高用户体验
  • 此时前端就算不做校验,也会在路由层就报错回去
  • 在这规定用户名是字母开头,字母数字下划线结尾。可自定义规则

ajv库

  • ajv库可帮助操作schema校验
  • 项目中安装ajv库npm i ajv --save
  • 使用示例使用示例
    • 参数1:schema校验规则
    • 参数2:需要校验的数据

步骤

  1. 创建验证工具: validator文件夹下新建_validate.js借助ajv创建 执行json schema 校验 的通用函数:_validate.js
  2. 进行user的数据格式校验:src下新建validator文件夹-新建user.js使用_validate.js中的函数传入json schema的校验规则需要校验的数据进行格式校验
    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
    /**
    * @description user 数据格式校验
    * @author hlz
    */

    const validate = require('./_validate')

    // 校验规则
    const SCHEMA = {
    type: 'object',
    properties: {
    userName: {
    type: 'string',
    pattern: '^[a-zA-Z][a-zA-Z0-9_]+$', // 字母开头,字母数字下划线
    maxLength: 255,
    minLength: 2
    },
    password: {
    type: 'string',
    maxLength: 255,
    minLength: 3
    },
    newPassword: {
    type: 'string',
    maxLength: 255,
    minLength: 3
    },
    nickName: {
    type: 'string',
    maxLength: 255
    },
    picture: {
    type: 'string',
    maxLength: 255
    },
    city: {
    type: 'string',
    maxLength: 255,
    minLength: 2
    },
    gender: {
    type: 'number',
    minimum: 1,
    maximum: 3
    }
    }
    }

    /**
    * 校验用户数据格式(data默认值为{})
    * @param {Object} data 用户数据
    */
    function userValidate(data = {}) {
    return validate(SCHEMA, data)
    }

    module.exports = userValidate
  3. 注册路由(src-routes-api-user.js)中添加一个中间件进行格式校验,先执行格式校验再执行注册操作。这个中间件在很多地方都有可能使用,所以我们先抽离中间件

抽离中间件(格式校验)

  1. 创建”生成json schema 验证中间件“的方法: src下新建middlewares文件夹-新建validator.jsvalidator.jsimage.png
  2. 注册路由中添加 验证中间件 进行格式校验: src-routes-users.js中:src-routes-users.js
  3. 测试:在前端我并没做密码长度的限制,但在路由中使用schema规则规定了密码长度最小是3:前端不报错,但无法提交所以不会走到注册,可以看到数据表中无增加数据

,