前言:用nodejs写一个前后端分离的入门级别博客。也是记录自己在这个平台第一次写文章。 这个项目分为登录注册篇、内容管理篇、评论管理篇以及去怎样部署一个node项目。
开始:1、创建目录--分层架构
2、因为我们的账户密码是私密的,部署到线上是不能给别人知道的。所以要写到另外文件上 比如我写在.env文件上
APP_PORT = 8082
MYSQL_HOST = 'localhost'
MYSQL_PORT = 3306
MYSQL_USER = 'root'
MYSQL_PASSWORD = '123456'
MYSQL_DATABASE = 'myblog'
JWT_SECRET_KEY = 'xxxx_666_xxxx...'
JWT_EXPIRE_TIME = 86400
3.读取配置代码config.js
const dotdev = require('dotenv'); // 导入 dotenv 模块解析配置文件
dotdev.config(); // 读取配置文件
// 导出配置信息
module.exports = {
APP_PORT,
MYSQL_HOST,
MYSQL_PORT,
MYSQL_DATABASE,
MYSQL_USER,
MYSQL_PASSWORD,
JWT_SECRET_KEY,
JWT_EXPIRE_TIME,
} = process.env
4.写好配置文件后在main.js入口文件引入koa库以及路由和其他中间件
const koa = require('koa')
const app = new koa()
require("./app/database") // 导入数据库连接
const bodyParser = require('koa-bodyparser'); // 解析请求体
const config = require("./app/config") // 导入配置
const userRoutes = require('./router') // 导入路由
app.use(bodyParser()) // 注册body-parser中间件
userRoutes(app) // 注册路由
// 启动服务
app.listen(config.APP_PORT, () => {
console.log(config.APP_PORT)
})
5、数据库代码编写database.js
const mysql2 = require('mysql2');
const config = require('./config');
const pool = mysql2.createPool({
host: config.MYSQL_HOST,
port: config.MYSQL_PORT,
user: config.MYSQL_USER,
password: config.MYSQL_PASSWORD,
database: config.MYSQL_DATABASE,
connectionLimit: 10,
});
// 测试连接
pool.getConnection((err, connection) => {
connection.connect(err=>{
if(err){
console.error('Error connecting to database: ', err);
}else{
console.log('Connected to database');
}
});
})
module.exports = pool.promise();
6.因为项目的路由比较多,所以采用动态路由读取。index.js
const fs = require('fs');
// 动态导入路由文件
const userRoutes = (app) => {
fs.readdirSync(__dirname).forEach((file) => {
if (file.endsWith('.js') && file!== 'index.js') {
const router = require(`./${file}`);
app.use(router.routes());
app.use(router.allowedMethods());
}
});
}
module.exports = userRoutes;
7、用户相关路由 userRouter.js
const Router = require('koa-router')
// 用户相关控制器
const {
createUser,
userLogin
} = require('../controller/userController')
// 用户相关中间件
const {
verifyUser,
verifyLogin,
verifyToken
} = require('../middleware/userMiddleware')
const router = new Router()
router.prefix('/user')
// 用户相关路由注册和登录
router.post('/create',verifyUser,createUser)
router.post('/login',verifyLogin,userLogin)
router.get('/test',verifyToken, async (ctx,next) => {
ctx.body = 'test'
})
module.exports = router
8、用户相关控制器 userController.js
const service = require('../services/userService');
const jwt = require('jsonwebtoken');
const config = require('../app/config');
class UserController {
// 创建用户
async createUser(ctx, next) {
// 获取用户请求参数
const user= ctx.request.body;
const result = await service.create(user);
ctx.body = result
}
// 用户登录
async userLogin(ctx, next) {
const {id, username} = ctx.user;
// 登录成功返回token
const token = jwt.sign({id, username},
config.JWT_SECRET_KEY,
{expiresIn: config.JWT_EXPIRE_TIME, algorithm: 'HS256'});
// 返回token
ctx.body = {
code: 200,
id,
username,
token,
message: `登录成功, 用户名为:${username}`
};
}
}
module.exports = new UserController;
9、控制器和逻辑分离目录,这样看起来比较清晰,在userService.js编写
const db = require('../app/database')
const { getDate,encryptPassword } = require('../utils/utils')
class UserService {
// 创建用户
async create(user) {
// 将用户保存到数据库中
let { username, password } = user
// 密码加密处理
password = encryptPassword(password)
// 获取当前时间
const create_time = getDate()
const sql = `INSERT INTO users (username, password,create_time) values (?,?,?)`
const result = await db.execute(sql, [username, password, create_time])
return result
}
// 根据用户名查找用户
async findByName(username) {
const sql = `SELECT * FROM users WHERE username = ?`
const result = await db.query(sql, [username])
return result[0]
}
// // 登录
// async login(user) {
// let { username, password } = user
// password = encryptPassword(password)
// const sql = `SELECT * FROM users WHERE username = ?`
// const result = await db.query(sql, [username, password])
// return result[0]
// }
}
module.exports = new UserService();
10、判断用户名以及密码是否为空,在中间件编写userMiddleware.js
const service = require('../services/userService')
const { encryptPassword } = require('../utils/utils')
const jwt = require('jsonwebtoken')
const config = require('../app/config');
// 注册验证中间件
const verifyUser = async (ctx, next)=>{
// 获取用户名和密码
const {username ,password} = ctx.request.body
// 判断用户名或者密码不能为空
if(!username || !password){
ctx.status = 400
ctx.body = {
code:"400",
message: '用户名或密码不能为空'
}
return
}
// 判断用户名是否存在
const result = await service.findByName(username)
if (result.length > 0) {
ctx.status = 400
ctx.body = {
code:"400",
message: '用户已存在'
}
return
}
// code to verify user
await next()
}
// 登录验证中间件
const verifyLogin = async (ctx, next) => {
// 获取用户名和密码
let { username, password } = ctx.request.body
// 判断用户名或者密码不能为空
if (!username || !password) {
ctx.status = 400
ctx.body = {
code:"400",
message: '用户名或密码不能为空'
}
return
}
// 判断用户名是否存在
const result = await service.findByName(username)
if (result.length === 0) {
ctx.status = 400
ctx.body = {
code:"400",
message: '用户不存在'
}
return
}
// 判断密码是否正确
const isMath = encryptPassword(password) === result[0].password
if (!isMath) {
ctx.status = 400
ctx.body = {
code:"400",
message: '密码错误'
}
return
}
ctx.user = result[0]
await next()
}
// 授权中间件
const verifyToken = async (ctx, next) => {
const authorization = ctx.headers.authorization
const token = authorization.replace('Bearer ', '')
if (!token) {
ctx.status = 401
ctx.body = {
code:"401",
message: '请登录'
}
return
}
try {
const decoded = jwt.verify(token,
config.JWT_SECRET_KEY,
{ algorithms: ['HS256']})
ctx.user = decoded
await next()
} catch (error) {
ctx.status = 401
ctx.body = {
code:"401",
message: "令牌错误或已过期"
}
}
}
module.exports = {
verifyUser,
verifyLogin,
verifyToken
}
11、工具函数 utils目录下编写
const crypto = require('crypto');
// 获取当前日期
function getDate(){
return new Date();
}
// 密码加密
function encryptPassword(password){
const result = crypto.createHash('md5').update(password).digest('hex');
return result;
}
module.exports = {
getDate,
encryptPassword
};
先这样,测试了 注册 :成功的返回一个insertId
判断是否注册
登录成功后返回用户信息和token
登录用户名不存在
登录密码错误
ps:如有写的不好,请指正,共勉。谢谢!会持续更新。