tips: node+mysql

创建用户

model.ts: 定义用户的类

1
2
3
4
5
export class UserModel{
id?:number;
name?:string;
password?:string
}

service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {connection} from '../app/database/mysql';
import {UserModel} from './user.model';

/**
* 创建用户
*/
export const createUser = async (user:UserModel)=>{
//准备查询
const statement=`
INSERT INTO user
SET ?
`;

//执行查询
const [data] = await connection.promise().query(statement,user);

//提供数据
return data;
}

controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import {Request,Response,NextFunction} from 'express';
import {UserModel} from './user.model';
import * as userService from './user.service';

export const store = async (
request:Request,
response:Response,
next:NextFunction
)=>{
//准备数据
const {name,password} = request.body;

//创建用户
try {
const data = await userService.createUser({name,password});
response.status(201).send(data);
} catch (error) {
next(error)
}
}

router.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import express from 'express';
import * as userController from './user.controller';

const router = express.Router();

/**
* 创建用户
*/
router.post('/users',userController.store);

/**
* 导出路由
*/
export default router;

验证用户数据

middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {Request,Response,NextFunction, request, response} from 'express';
import * as userService from './user.service';

/**
* 验证用户数据
*/
export const validateUserData = async (
request:Request,
response:Response,
next:NextFunction
)=>{
console.log('验证用户数据');

//准备数据
const {name,password} = request.body;

//验证必填数据
if(!name) return next(new Error('NAME_IS_REQUIRED'));
if(!password) return next(new Error('PASSWORD_IS_REQUIRED'));

//下一步
next();
}

router 使用的时候:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from 'express';
import * as userController from './user.controller';
import {validateUserData} from './user.middleware';

const router = express.Router();

/**
* 创建用户
*/
router.post('/users',validateUserData,userController.store);

/**
* 导出路由
*/
export default router;

处理创建用户的错误

在 app.middleware.ts 中:

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
import { Request, Response, NextFunction } from 'express';

/**
* 默认异常处理器
*/
export const defaultErrorHandler = (
error: any,
request: Request,
response: Response,
next: NextFunction,
) => {
if(error.message){
console.log(error.message)
}
let statusCode: number, message: string;

/**
* 处理异常
*/
switch (error.message) {
case 'NAME_IS_REQUIRED':
statusCode = 400;
message = '请提供用户名';
break;
case 'PASSWORD_IS_REQUIRED':
statusCode = 400;
message = '请提供用户密码';
break;
default:
statusCode = 500;
message = '服务暂时出了点问题~';
break;
}
response.status(statusCode).send({ message });
};

定义按用户名查找用户的服务

user.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 按用户名查找用户
*/
export const getUserByName = async (name:string)=>{
//准备查询
const statement = `
SELECT id, name
FROM user
WHERE name = ?
`

//执行查询
const [data] = await connection.promise().query(statement,name);

//提供数据
return data[0];
}

注册时检查用户名是否存在

user.middleware.ts:

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
import {Request,Response,NextFunction, request, response} from 'express';
import * as userService from './user.service';

/**
* 验证用户数据
*/
export const validateUserData = async (
request:Request,
response:Response,
next:NextFunction
)=>{
console.log('验证用户数据');

//准备数据
const {name,password} = request.body;

//验证必填数据
if(!name) return next(new Error('NAME_IS_REQUIRED'));
if(!password) return next(new Error('PASSWORD_IS_REQUIRED'));

//验证用户名
const user = await userService.getUserByName(name);
if(user) return next(new Error('USER_ALREADY_EXIST'));

//下一步
next();
}

app.middleware.ts:

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
import { Request, Response, NextFunction } from 'express';

/**
* 默认异常处理器
*/
export const defaultErrorHandler = (
error: any,
request: Request,
response: Response,
next: NextFunction,
) => {
if(error.message){
console.log(error.message)
}
let statusCode: number, message: string;

/**
* 处理异常
*/
switch (error.message) {
case 'NAME_IS_REQUIRED':
statusCode = 400;
message = '请提供用户名';
break;
case 'PASSWORD_IS_REQUIRED':
statusCode = 400;
message = '请提供用户密码';
break;
case 'USER_ALREADY_EXIST':
statusCode = 409;
message = '用户名已被占用';
break;
default:
statusCode = 500;
message = '服务暂时出了点问题~';
break;
}
response.status(statusCode).send({ message });
};

存储密码

在数据仓库里直接存储用户设置的密码,会有很严重的安全问题,一般会用 hash 来处理密码数据,bcrypt 就是其中的一种 hash 方法,为了让密码更难猜到,在 hash 的时候会加点盐,盐就是随机的一串字符,把加了盐的字符再用 hash 方法处理下,这样就很难猜到了。在数据仓库中存储的是经过 hash 处理后的密码,顺便还要存储下盐。


hash 用户密码(bcrypt)

在 node 生态中有一个 bcrypt 的包,它里面提供了用 bcrypt 这种方法 hash 数据,然后比对 hash 数据的方法。
安装:

1
npm install bcrypt

使用了 typescript 后还需要安装一下类型定义:

1
npm install @types/bcrypt --save-dev

user.middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import bcrypt from 'bcrypt';
/**
* HASH 密码
*/
export const hashPassword = async (
request:Request,
response:Response,
next:NextFunction
)=>{
// 准备数据
const {password} = request.body;

//HASH密码
request.body.password = await bcrypt.hash(password,10);

//下一步
next();
}

user.router.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from 'express';
import * as userController from './user.controller';
import {validateUserData,hashPassword} from './user.middleware';

const router = express.Router();

/**
* 创建用户
*/
router.post('/users',validateUserData,hashPassword,userController.store); //在这里添加中间件

/**
* 导出路由
*/
export default router;