KOA 入门,简单实现用户注册和登录逻辑

koa

首先来介绍一下什么是 koa

koa 是由 Express 背后的团队设计的一个新的 Web 框架,旨在成为 Web 应用和 API 的更小、更具表现力和更强大的基础。 通过利用异步函数,koa 允许你放弃回调并大大提高错误处理能力。 koa 的核心中没有捆绑任何中间件,它提供了一套优雅的方法,使编写服务器变得快速而愉快。

官网中文版

基本用法

安转

npm i koa koa-router koa-static koa-views

基本配置

const path = require('path')
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const static = require('koa-static')
const views = require('koa-views')

// 加载模版引擎
app.use(views(path.join(__dirname, './views'), {
    extension: 'ejs'      // 模版引擎类型
}))

// 配置静态文件
app.use(static(path.join(__dirname, './static')))

// 设置路由
const router = new Router()
router.get('/', async (ctx) => {
    ctx.body = 'hello world'
})

app.use(router.routes(), router.allowedMethods())

app.listen(4000)

按照上面,我们可以继续添加已经封装好的第三方中间件来实现我们所需要的功能。但是下面我们使用一个 koa 的脚手架工具来进行搭建我们的后端服务。

正式开始

下载脚手架和初始化 koa项目

npm install koa-generator -g
koa2 -e koa-cli
cd koa-cli
npm i

koa2 表示用 koa2.x的版本; -e 是指使用 ejs 模版引擎; koa-cli 是项目名称,可以设置自己的 project-name

初始化后,我们用 vscode 打开后应该展示到的目录结构是这样的

这里的目录结构我先不做过多的描述了,接下来直接使用 npm run dev 就可以看到程序跑了起来。在浏览器里访问 localhost:3000,就可以看到欢迎页面了。

好了,本文到此结束(并不是)

接下来我们来连接下数据库,实现一下注册登录等逻辑。

真·正式开始

整理项目目录「也可以不用整理,看个人习惯」

首先我们来整理下项目的目录,首先我们在根目录下创建一个 src 的文件夹,然后将 public文件routes文件views文件 以及 app.js 移到 src 目录中,同时 bin 文件中的 www 文件中的对应引用也要做对应的修改。

// var app = require('../app');    // 旧
var app = require('../src/app');   // 新

连接数据库

我们这里选择 mysql 数据库,所以要先在本地安装对应的 mysql 数据库。我这里就默认大家安装好了哈。

node 中,连接 mysql 数据库的方法有很多种,其中就有 mysqlmysql2sequelize

这里的 mysql 是 npm 中的第三模块,并不是指 mysql 数据库

这里我们使用 sequelize

下载安装 sequelize

# 其中 sequelize 中依赖了 mysql2,所以要一并安装 mysql2
npm i sequelize mysql2

然后我们在 src 下创建一个 db 文件夹,用来存放数据库相关。

// src > db > index.js
const Sequelize = require('sequelize')

// 这里创建 sequelize 实例
const sequelize = new Sequelize(
    'koa_cli_db',                   // database
    'root',                         // 数据库用户
    '',                             // 密码
    {
        host: 'localhost',          // 地址
        dialect: 'mysql'            // 数据库类型
    }
)

module.exports = sequelize

这里有一点要注意,database 一定要是真实存在的,所以我们要先在数据库中创建对应的 database。

可以使用 CREATE SCHEMA "koa_cli_db" 来创建。或者可以下载可视化操作工具来进行操作。例如:NavicatMySQL Workbench

new Sequelize 中传入数据库相关配置,但是一般我们会将相关配置提取出来。这里我们将相关信息提出。src 下创建 config 文件夹,存放相关配置。然后创建 _db.js 文件存放相关配置。

// src > config > _db.js
const SQL_CONFIG = {
    database: 'koa_cli_db',
    root: 'root',
    password: '',
    host: 'localhost'
}

module.exports = {
    SQL_CONFIG
}

// src > db > index.js
const Sequelize = require('sequelize')
const { SQL_CONFIG } = require('../config/_db')

const sequelize = new Sequelize(SQL_CONFIG.database, SQL_CONFIG.root, SQL_CONFIG.password, {
    host: SQL_CONFIG.host,
    dialect: 'mysql'
})

完成这一步后,我们来测试是否连接成功数据库,db 文件夹下创建 connect.js

const sequelize = require('./index')

/** sql 链接测试 */
sequelize.authenticate().then(() => {
    console.log('连接数据库成功!')
}).catch(err => {
    console.log(err)
    console.log('连接数据库失败!')
})

然后终端执行 node ./src/db/connect.js 来测试。显示

则说明我们连接数据库成功啦。

建表

连接数据库后,我们就要建表了。sequlize 是一个基于 promise 的 Node.js ORM(对象关系映射)工具。所以对于 sequelize 来说,建表是通过定义对应的对象(我们称模型)来操作的。接下来我们来定义模型,包括字段和关联关系。

首先我们在 db 下创建一个 model 文件夹,用来存放我们定义的模型。在 model 下创建我们第一个模型文件 User.js

// src > db > model > User.js
const sequelize = require('../index')
const { STRING, TEXT } = require('sequelize')

const User = sequelize.define('user', {
    // 这里面相当于表的每一列
    userName: {
        type: STRING,           // 数据类型,sequelize 提供的数据类型有:STRING, BOOLEAN, TEXT, INTEGER, DECIMAL等
        allowNull: false,       // 是否允许为空
        comment: '用户名'        // 描述
    },

    password: {
        type: STRING,
        allowNull: false,
        comment: '密码'
    },

    introduce: {
        type: TEXT,
        comment: '描述'
    },

    email: {
        type: STRING,
        allowNull: false,
        comment: '邮箱'
    }
})

module.exports = User

如此,我们就创建了 User 模型,接下来我们要用模型创建对应的 mysql中对应的table

我们这里就直接在 connect.js 文件中直接操作。

// src > db > connect.js

// ... 其他代码

// 引入 User 模型,这里因为只有一个,我就直接引入了。
// 如果是模型很多的情况下,我们可以在 model 中创建 index.js 来做唯一出口,收集所有的 model 并统一导出
require('./model/User')

/** model 同步数据库 */
// 强制同步,会将原先的表格以及数据清空,重新创建新的表格
sequelize.sync({ force: true }).then(() => {
    console.log('同步数据库成功')
    process.exit()
})

/**
 * 标准同步
 * sequelize.sync() 
 * 只有数据库中不存在与模型同名的数据表时才同步
 */

/**
 * 动态同步
 * sequelize.sync({alter: true})
 * 修改同名数据表结构
 */

我们这里使用强制同步,其他同步方式也列举出来了,按需选择

终端执行 node ./src/db/connect.js,然后我们就可以看到

提示成功同步,然后我们就可以在 MySQL Workbench 中看到对应的表。

这里可以看到,User 模型同步后,创建了一个 users 表,里面不仅包括我们设置的属性,还有 idcreatedAtupdatedAt 属性。

相关说明:

  • User 模型同步表,对应的 表名sequlize.define() 中第一个参数的复数。意思就是创建表的时候会自动加上 s。因为先前设置的是 user,所以这里表名是 users
  • 创建的表的同时,除了我们定义的属性,sequelize 会自动帮我们设置 id,并设置为 主键。同时还有 createdAtupdatedAt。当然我们也可以手动设置 主键,可以通过配置 primaryKey: true 来实现。

实现注册和登录

表格创建完后,我们接下来就要去添加数据,首先我们实现一下注册用户。

首先我们在 src 目录下创建 service 文件夹,存放模型相关操作

// src > service > user.js
const User = require('../db/model/User')

/**
 * 新增用户
 */
async function createUser({ userName, password, email, introduce }) {
    const res = await User.create({
        userName,
        password: password,
        email,
        introduce,
    })

    const data = res.dataValues
    return data
}

// 测试,测试完后记得删掉或注释
createUser({
    userName: 'limoonrise',
    password: 'limoonrise',
    email: 'limoonrise@163.com',
    introduce: '测试测试'
})

module.exports = {
    createUser
}

终端运行 node ./src/service/user.js,然后就能在 users 中查看到相关记录。

接着我们先创建一下注册接口。routes 文件夹 下创建一个 api 文件夹,这里存放相关接口文件

// src > routes > api > user.js
const router = require('koa-router')()
// 注册接口
router.post('/api/user/register', async(ctx, next) => {
    const { userName, password, email, introduce } = ctx.request.body
    ctx.body = {
        userName,
        password,
        email,
        introduce
    }
})
module.exports = router

然后还要去 app.js 中引用

// src > app.js

const userAPI = require('./routes/api/user')
// routes
app.use(userAPI.routes(), userAPI.allowedMethods())

这时可以用 postman 测试下接口,可以看到对应的传值。

接着我们开始写相关逻辑,先在 src 下创建一个 controller 文件夹,存放相关逻辑处理

const { createUser } = require('../service/user')

async function register({ userName, password, email, introduce }) {
    // controller 里面可以处理很多,比如在创建用户前判断是否已经存在该用户等
    // service
    try {
        const res = await createUser({
            userName,
            password,
            email,
            introduce,
        })
        
        return {
            code: 0,
            data: res
        }
    } catch (err) {
        console.log(err)
        return {
            code: 1,
            message: '创建用户失败'
        }
    }
}

module.exports = {
    register
}

最后,我们回归到 routes

router.post('/api/user/register', async(ctx, next) => {
    const { userName, password, email, introduce } = ctx.request.body
    ctx.body = await register({
        userName,
        password,
        email,
        introduce
    })
})

这时我们在通过 postman 去测试

这里只是简单实现了注册,现实中还是有很多额外的操作的,比如注册的时候判断注册用户是否已经存在,以及密码注册的时候进行加密处理,而不是直接明文的存进数据库

实现完注册,登录的逻辑就相对比较简单了

首先我们先实现 service

// src > service > user.js
async function findOneUser({ userName, password }) {
    const res = await User.findOne({
        attributes: ['userName', 'introduce', 'email', 'id'],       // 获取哪些属性
        where: {
            userName,
            password
        }
    })

    if (res) return res
    return 0
}

module.exports = {
    createUser,
    findOneUser
}

controller

// src > controller > user.js
const { createUser, findOneUser } = require('../service/user')

async function userLogin({ userName, password }) {
    const res = await findOneUser({userName, password})
    if (res) {
        return {
            code: 0,
            data: res,
            message: '登录成功'
        }
    } else {
        return {
            code: 2,
            message: '登录失败'
        }
    }
}

module.exports = {
    register,
    userLogin
}

最后 routes

// src > routes > api > user.js

// 添加 prefix,记得 注册接口 '/api/user/register' 要改成 ‘/register’
router.prefix('/api/user')

router.post('/login', async(ctx, next) => {
    const { userName, password } = ctx.request.body

    ctx.body = await userLogin({ userName, password })
})

ok,我们来测试一下

到这里,我们登录也完成了,这里还是只是简单实现了一下。至于其他的操作,比如,我们登录后,要怎么记录用户的登录状态等,有机会再说~

posted @ 2024-11-21 18:22  limoonrise  阅读(99)  评论(0)    收藏  举报