用户管理模块
Posted 昵称正在加载中....
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用户管理模块相关的知识,希望对你有一定的参考价值。
一、配置前后端路由
- 界面路由
router.get('/login', async (ctx, next) => {
await ctx.render('login', {})
})
router.get('/register', async (ctx, next) => {
await ctx.render('register', {})
})
- 后端接口路由
router.prefix('/api/user')
//注册
router.post('/register', async (ctx, next) => {
const { userName, password, gender } = ctx.request.body
//controller
ctx.body = await register({ userName, password, gender })
})
//登录
router.post('/login', async (ctx, next) => {
const { userName, password } = ctx.request.body
//controller
ctx.body = await login(ctx, userName, password)
})
//用户名是否存在
router.post('/isExist', async (ctx, next) => {
const { userName } = ctx.request.body
console.log("username",userName)
//controller
ctx.body = await isExist(userName)
})
注:
(1)还要注册路由接口,这里没有贴图忽略
(2)前端页面采用ejs模板,后面会分享出来
(3)这里主要涉及的是后端的配置
二、功能一:用户名是否存在
- 功能流程图
- controller 层
/**
*
* @param {String} userName 用户名
*/
async function isExist(userName) {
//调用 services 获取数据
const userInfo = await getUserInfo(userName)
if (userInfo) {
//已存在
return new SuccessModel()
} else {
//不存在
return new ErrorModel(registerUserNameNotExistInfo)
}
}
- services 层
async function getUserInfo(userName, password) {
//查询条件
const whereOpt = {
userName
}
if (password) {
//ES6 对象语法 拷贝对象
Object.assign(whereOpt, {password})
}
//查询
const result = await User.findOne({
attributes:[
'id',
'userName',
'nickName',
'picture',
'city'
],
where:whereOpt
})
if (result == null) {
//未找到
return result
}
//格式化
const formaRes = formatUser(result.dataValues)
return formaRes
}
三、功能二:用户注册
- 功能流程图跟第二点一样(采用分层的思想)
- controller 层
async function register({ userName, password, gender }) {
//用户是否存在
const userInfo = await getUserInfo(userName)
if (userInfo) {
//用户名已存在
return ErrorModel(registerUserNameExistInfo)
}
//注册 service
try {
await createUser({
userName,
password: doCrypto(password),//md5加密
gender
})
return new SuccessModel()
} catch (ex) {
console.error(ex.message, ex.stack)
return new ErrorModel(registerFailInfo)
}
}
- service 层
async function createUser({ userName, password, gender = 3, nickName}){
const result = User.create({
userName,
password,
nickName: nickName ? nickName :userName,
gender
})
return result.dataValues
}
-
功能测试
-
涉及的知识点:
(1)上面的三点知识模块
(2)md5 加密不懂点这里
四、前端发送用户信息对其格式校验
- 安装 ajv 插件
npm i ajv – save
- 配置用户信息格式
// 校验规则 user.js
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
}
}
}
- 封装校验方法
//validate.js
const Ajv = require('ajv').default
const ajv = new Ajv()
/**
* json schema 校验
* @param {Object} schema json schema 规则
* @param {Object} data 待校验的数据
*/
function validate(schema, data = {}) {
const valid = ajv.validate(schema, data)
if (!valid) {
return ajv.errors[0]
}
}
module.exports = validate
主: 代码中导入 ajv 包后面需要加 default,否则程序会报 new Ajv() 不是一个构造器
-
调用封装的方法
-
将 user.js 中的 UserValidate 封装成中间件
const userValidate = require("../validator/user");
/**
* 生成 json schema 验证的中间件
* @param {function} validatorFn 验证函数
* @returns
*/
function genValidator(validatorFn) {
//定义中间件函数
async function validator(ctx, next) {
//校验
const data = ctx.request.body
const error = validatorFn(data)
if (error) {
ctx.body = new ErrorModel(jsonSchemaFileInfo)
return
}
await next()
}
//返回中间件函数
return validator
}
module.exports = {
genValidator
}
- 路由层api中,使用该中间件
const { genValidator } =require('../../middlewares/validator')
const userValidate = require('../../validator/user')
//注册
router.post('/register',genValidator(userValidate), async (ctx, next) => {
const { userName, password, gender } = ctx.request.body
//controller
ctx.body = await register({ userName, password, gender })
})
- 涉及到的知识模块
(1)JSON schema 数据校验转载:不懂点这里
五、功能三:用户登录
- router 层 和 controller 层 跟前面俩个功能模块类似,不细讲
- service 层是直接调用判断用户是否存在的方法(getUserInfo),只是要在传递一个密码值即可。
六、封装登录验证的中间件(页面、api)
- 中间件
/**
* API 登录验证
* @param {Object} ctx
* @param {function} next
*/
async function loginCheck(ctx, next){
if (ctx.session && ctx.session.userInfo) {
await next()
return
}
//未登录
ctx.body = new ErrorModel(loginCheckFailInfo)
}
/**
* 页面登录验证
* @param {Object} ctx
* @param {function} next
*/
async function loginRedirect(ctx, next){
if (ctx.seession && ctx.session.userInfo) {
await next()
}
const curUrl = ctx.url
ctx.redirect('/login?url=' + encodeURIComponent(curUrl))
}
module.exports = {
loginRedirect,
loginCheck,
}
- 在要使用登录验证的路由中使用即可 如:
//删除
router.post('/del', loginCheck, async (ctx, next) => {
if (isTest) {
//测试环境下,测试账号登录之后,删除自己
const { userName } = ctx.session.userInfo
//调用 controller
ctx.body = await deleteCurUser(userName)
}
})
七、进行单元测试
/**
* @description jest server
* @author 浩
*/
const request = require('supertest')
const server = require('../src/app').callback()
module.exports = request(server)
/**
* @description user api test
* @author 浩
*/
const server = require('../server')
// 用户信息
const userName = `u_${Date.now()}`
const password = `u_${Date.now()}`
const testUser = {
userName,
password,
nickName:userName,
gender:1,
}
// 存储cookie
let COOKIE = ''
// 注册
test('注册一个用户,应该成功', async() => {
const res = await server
.post('/api/user/register').send(testUser)
expect(res.body.errno).toBe(0)
})
// 重复注册
test("重复注册,应该失败", async () => {
const res = await server
.post('/api/user/register').send(testUser)
expect(res.body.errno).not.toBe(0)
})
// 查询注册的用户名是否存在
test("查询注册的用户名,应该存在", async () => {
const res = await server
.post('/api/user/isExist')
.send({userName})
expect(res.body.errno).toBe(0)
})
// json schame 校验
test("json schame 检测,非法的格式,注册应该失败", async () => {
const res = await server
.post('/api/user/register')
.send({
userName: '123', //用户名不是字母(或下划线)开头
password: 'a', // 最小的长度不是 3
gender: 'mail' // 不是数字
})
expect(res.body.errno).not.toBe(0)
})
//登录
test('登录,应该成功', async () => {
const res = await server
.post('/api/user/login')
.send({
userName,
password
})
expect(res.body.errno).toBe(0)
//获取cookie
COOKIE = res.headers['set-cookie'].join(';')
})
// 删除
test('删除用户,应该成功', async () => {
const res = await server
.post('/api/user/del')
.set('Cookie', COOKIE)
expect(res.body.errno).toBe(0)
})
// 再次查询注册的用户是否存在,应该不存在
test('再次查询用户,应该不存在', async () => {
const res = await server
.post('/api/user/isExist')
.send({
userName
})
expect(res.body.errno).not.toBe(0)
})
- 这部分与上面的信息格式校验类似,不同的是:前者是上线后一直都会校验的,而后者是在开发过程中,为了防止误删某个属性,程序员不知道。
const { User } = require('../../src/db/model/index')
test('User 模型的各个属性,符合预期', () => {
// build 会构建一个内存的 User 实例,但不会提交到数据库中
const user = User.build({
userName: 'zhangsan',
password: 'p123123',
nickName: '张三',
picture: '/xxx.png',
city: '汕头',
// gender: 1
})
//验证属性
expect(user.userName).toBe('zhangsan')
expect(user.password).toBe('p123123')
expect(user.nickName).toBe('张三')
expect(user.picture).toBe("/xxx.png")
expect(user.city).toBe('汕头')
expect(user.gender).toBe(3)
})
以上是关于用户管理模块的主要内容,如果未能解决你的问题,请参考以下文章
如何使用模块化代码片段中的LeakCanary检测内存泄漏?
《java精品毕设》基于javaweb宠物领养平台管理系统(源码+毕设论文+sql):主要实现:个人中心,信息修改,填写领养信息,交流论坛,新闻,寄养信息,公告,宠物领养信息,我的寄养信息等(代码片段