koa服务端项目搭建
Koa 是一个基于 Node.js 的 Web 开发框架,它的目标是让 Web 开发更简单、更有表现力和更健壮。Koa 借鉴了 Express 框架的中间件的精髓,提供了更加简洁和优雅的代码风格,支持 async/await
等新型语法。
如果文章中有什么介绍的不清楚的地方,可以访问 本项目github地址 通过看完整的项目代码结合本文进行阅读
一、项目初始化
1.安装koa
yarn koa
2.创建app.js
import Koa from 'koa'
const app = new Koa()
app.on('error', (err, ctx) => {
console.error(err);
});
// 监听端口
const server = app.listen(3000,'127.0.0.1',()=>{
const address = server.address()
console.log(`服务启动成功!请访问${address.address}:${address.port}`);
})
3.控制台启动服务
在项目根目录下打开控制台输入命令
node app.js
打印出服务信息代表服务启动成功
二、压缩编译代码
1.安装webpack
yarn add -D webpck webpack-cli webpack-node-externals
其中webpack-node-externals是用于在 Webpack 的 Node.js 环境中排除 Node.js 内置模块和已安装的第三方模块,以减小打包文件的体积,减轻浏览器的加载负担。
2.安装babel插件
yarn add -D @babel/core @babel/node @babel/preset-env babel-loader
babel插件用于将es6代码编译成es5代码。 在根目录下创建.babelrc文件,它是 Babel 的配置文件,可以用于指定 Babel 的转换规则和插件等,
3.配置webpack
创建webpack.config.js
const nodeExternals = require("webpack-node-externals")
module.exports = {
// target 设置为 node,webpack 将在类 Node.js 环境编译代码,使用 Node.js 的 require 加载 chunk,而不加载任何内置模块,如 fs 或 path
target:"node",
// 打包入口
entry:"./app.js",
// 输出文件
output:{
filename:"app.js",
// 清除之前打包的文件
clean:true,
},
externals:[nodeExternals()],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
}
4.配置package.json
在package.json中加入下面几行代码,用于定义项目中常用的命令,其中我们使用nodomon
替换了node
,nodemon
是一个基于 Node.js 开发的工具,使用 nodemon
可以让我们在修改文件后,自动重启 Node.js 应用,从而省去手动重启的麻烦。如果没有安装nodemon
,可以使用sudo yarn -g nodemon
全局安装一下。
"scripts": {
"dev": "nodemon --exec babel-node ./app.js",
"build": "npx webpack build --mode=production"
},
yarn dev
是开发时启动的命令,在控制台执行后会在我们本地启动服务
yarn build
是打包编译的代码,执行build后会自动创建出一个dist目录。目录中的文件就是编译后的代码。
三、创建项目目录结构
在项目的根目录下创建下面的文件夹,用于拆分细化项目的各个功能及模块
├── config // 配置文件目录
├── controller // 控制器,也就是业务逻辑代码,路由接受到请求后处理的代码
├── core // 核心代码
├── middlewares // 自定义中间件目录
├── public // 存放静态文件的目录
├── router // 路由
├── models // 数据模型
├── utils // 工具集
├── services // 该目录存放对数据模型的操作的代码
├── validators // 校验前端传过来数据的代码
四、中间件
1.koa-static
使用yarn add koa-static
将koa-static添加到项目中,koa-static
是一个 Koa
框架的中间件,用于提供静态文件服务,在app.js
文件中加入以下代码,就可以实现通过URL对pulic
目录下的静态资源的访问,例如public
目录下有一个index.html
文件,那么我们在本地启动服务后就可以通过http://localhost:3000/index.html
访问到这个文件。
import KoaStatic from 'koa-static'
// 静态资源访问
app.use(KoaStatic(path.join(path.resolve(),'/public')))
2.koa-router
使用yarn add @koa/router
将@koa/router添加到项目中
假如我们要创建用户的接口,在router
目录中创建user.js,并添加以下代码
import Router from '@koa/router'
// 创建路由对象
const router = new Router({
// 路由前缀
prefix:"/user"
})
router.get("/info",(ctx,next)=>{
ctx.body = "用户信息"
})
export default router
在app.js中加入
import userRouter from './router/user'
app.use(userRouter)
这样我们就能通过 http://localhost:3000/user/info 访问到这个接口
3.koa-body
使用yarn add koa-body
将koa-body添加到项目中,用于解析 HTTP
请求主体,可以从提交的表单中提取文件和任何其他数据。可以使用 koa-body
来处理各种 HTTP
请求,例如 POST
,PUT
和 PATCH
等请求。koa-body
可以解析 JSON
,Urlencoded
, Multiparty
, Form
和 Text
等多种请求主体格式,可以根据需要进行配置。如果只需要解析简单的表单数据和JSON数据,可以使用KoaBodyParser;如果需要处理更加复杂的请求体,包括文件上传等,就应该选择KoaBody。
在app.js中加入
import KoaBody from 'koa-body';
// 接受请求参数
// app.use(KoaBodyparser())
app.use(KoaBody())
在router/user.js目录中加入
router.post("/create",(ctx,next)=>{
// 打印post参数
console.log(ctx.request.body)
})
也可以通过它实现文件上传代码如下
// 文件上传
router.post("/upload",koaBody({
multipart: true,
formidable:{
// 限制字段数
maxFields:1,
// 上传文件目录
uploadDir: path.join(path.resolve(),'/public/upload'),
// 最大的文件大小
maxFileSize:1*1024*1024,
// 包含源文件拓展
keepExtensions: true,
// 是否支持上传多个文件,默认为true
multiples:false,
// 自定义文件名
filename:(name,suffix)=>`${Date.now()}-${name}${suffix}`
}
}),(ctx,next)=>{
ctx.body={
code:200,
data:ctx.request.files,
message:"上传成功"
}
})
4.koa-log4
使用yarn add koa-log4
将koa-log4添加到项目中,用于记录 HTTP
请求和响应信息,可以方便地在开发和生产过程中进行调试和日志分析。它是基于 log4js
模块构建的。具体用法
5.koa-bouncer
使用yarn add koa-bouncer
将koa-bouncer添加到项目中,用于验证和转换 Koa
应用程序中的请求数据的中间件。它提供了一组简单的验证器,可以轻松地验证 JSON
和 表单数据,也可以将数据解析成指定的数据类型。具体用法
6.koa-onerror
使用yarn add koa-onerror
将koa-bouncer添加到项目中,koa-onerror
是一个 Koa
框架中间件,用于处理程序的错误和异常。它可以捕获 Koa
应用程序的错误和异常并自动处理,展示友好的错误信息和提示,帮助开发人员更快地定位问题。
7.koa-session
使用yarn add koa-session
将koa-session添加到项目中,用于在服务端存储和管理会话(session)数据。会话是一种跨请求的数据保存技术,它使得我们在多个请求之间共享数据变得更加简单。通过使用 koa-session
中间件,可以在服务端为每个客户端创建一个cookie存储服务端(session)数据的sid,服务端(session)中存储需要共享的数据。客户端会自动在后续请求中携带这个cookie,以便服务端通过这个cookie中的sid检索出对应的session的数据,既可以通过它存储数据也可以用来登录鉴权
7.1 session登录鉴权
在根目录下app.js添加下面代码:
import KoaSession from 'koa-session';
// 对cookie中的sid进行加密签名,xxxx可以替换成你自己定义的字符串
app.keys = ['xxxx']
// session 配置
const SESSION_CONFIG = {
key:"key", // 设置cookie的key的名字
maxAge:1000*60*60*12, // 设置有效期为12个小时
httpOnly: true, // 仅服务端修改
signed:true,// 签名cookie
}
app.use(KoaSession(SESSION_CONFIG,app))
然后再写一个登录接口,代码如下:
// 登录-session
router.post("/login",(ctx,next)=>{
// 获取请求参数
const {username,password} = ctx.request.body
// 校验用户名密码
if(body.username=="TC"&&body.password=="123456"){
ctx.session.isLogin = true
ctx.session.username = body.username
ctx.body={
code:200,
message:"登录成功"
}
}else{
ctx.body={
code:400,
message:"登录失败!用户名或密码错误"
}
ctx.status = 400
}
})
这样就成功登录成功了,那如何鉴权呢?还需要继续封装一个鉴权的中间件,代码如下:
// 不需要鉴权的白名单接口
const whiteApis = ['/login']
// 鉴权中间件
const sessionAuth = async (cxt,next)=>{
const {path,method} = cxt.request
// 判断接口是否需要鉴权
const pass =whiteApis.includes(path)
if(pass){ // 如果不需要直接向下执行
await next()
}else{ // 如果需要,判断是否登录
if(cxt.session.isLogin){ // 如果登录继续向下执行
await next()
}else{ // 如果没登录,直接返回鉴权失败信息
cxt.body = {
code:401,
message:"用户未登录"
}
}
}
}
鉴权中间加写好了,我们直接router.use(sessionAuth)
,这样就可以使用了,但是注意一定要放在所有需要鉴权接口的前面,这样才能先走鉴权中间件,鉴权完毕才能调用鉴权接口。session的登录鉴权就OK了,还有一种token鉴权方式会在下面的内容里介绍。
7.2 svg-captcha生成验证码
使用yarn add svg-captcha
将svg-captcha添加到项目中,它可以轻松生成验证码并返回 SVG 格式的图片。还可以生成算数计算的验证码。具体实现请查看 svg-captcha中文文档。
下面我们就简单使用一下。代码如下:
// 验证码接口
router.get("/vercode",(cxt,next)=>{
let option = {
size:4, // 验证码长度
ignoreChars: '0o1i', // 验证码字符中排除 0o1i
noise: 6, // 干扰线条的数量
color: true, // 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
// background: '#cc9966', // 验证码图片背景颜色
// charPreset:"23456789" // 预设字符,二维码只能从预设的字符中生成
}
// 生成简单验证码
var captcha = svgCaptcha.create(option)
// 将验证码结果字符串存入session中
cxt.session.vercode = captcha.text
cxt.type = "svg"
cxt.body=captcha.data
})
这样我们就可以在浏览器地址栏里输入 http://localhost:3000/vercode 就可以看到一张验证码,每次请求都会重新生成一张,在登录接口里。我们可以通过cxt.session.vercode
取出验证码与接口请求验证码参数对比一下看是否相等
五、MongoDB数据库使用
我们这里使用的数据库是MongoDB数据库,只需要创建一个数据库就可以了,无须手动一个一个的创建表。项目中我们使用mongoose
工具来操作是数据库,mongoose
是一个 Node.js
应用程序的对象模型工具,它使得在 MongoDB
中的数据管理变得更加容易和灵活。它提供了一种类似于面向对象语言的方式来管理数据,同时也包含了非常强大的查询和更新功能。mongoose的中文文档在这 mongoose中文网
1.数据库连接
具体代码如下:
import mongoose from 'mongoose';
import config from '../config/index' //引入项目配置文件
// config.user 是MongoDB数据库的用户名
// config.password 是MongoDB数据库的用户密码
// config.ip 是MongoDB数据库的所在的服务器ip,如果数据库本地就用localhost
// config.port 是MongoDB数据库的端口号,一般都是27017
// config.name 是MongoDB数据库的数据库名称
// 数据库连接 mongoose.connect(`mongodb://${config.user}:${config.password}@${config.ip}:${config.port}/${config.name}`,{
// 是否在连接过程中使用新的 URL 解析器
useNewUrlParser:true,
// 先从admin表中验证数据库用户身份才行
authSource:"admin",
// 表示是否使用新的、基于 Node.js 的拓扑结构监视引擎
useUnifiedTopology:true,
})
mongoose.connection.on("error",(err)=>{
// 打印错误信息
console.log(err)
})
mongoose.connection.on("open",(err)=>{
console.log("数据库连接成功")
})
2.创建数据模型
例如我们要创建一个用户文档的数据模型,就在mondels文件中创建一个user.js文件,bcrypt
可以将用户的密码进行加密后再保存进数据库中,具体使用请查看 brypt使用,代码如下:
import {Schema,model} from 'mongoose'
import bcrypt from 'bcrypt'
const schema = new Schema({
username:{
type:String,
required:[true,'用户名为必传参数'],
unique: true, //用户名不能重复
// 去掉两头空格
trim:true
},
password:{
type:String,
required:[true,'密码为必传参数'],
// 查询时不返回
select: false,
trim:true
},
phone:{
type:String,
required:true,
validate: {
validator: function(v) {
return /\d{3}-\d{3}-\d{4}/.test(v);
},
message: props => `${props.value} 格式错误`
},
required: [true, '手机号必传'],
},
})
// 密码加密
schema.pre('save',function(next){
var user = this;
//产生密码hash当密码无更改的时候
if (!user.isModified('password')) return next();
// 产生一个盐值(salt)
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
// 使用盐值(salt)和明文密码进行哈希加密处理
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// 使用hash覆盖明文密码
user.password = hash;
next();
});
});
})
// 密码对比校验
schema.method.comparePass = function (password,callback){
bcrypt.compare(password,this.password,(isMatch)=>{
if(err) return callback(err)
callback(null,isMatch)
})
}
export default model("User",schema)
3.数据库操作
在services文件夹中创建一个user.js,代码如下:
import User from "../modals/user"
// 创建用户
const create=async ({username,phone,password})=>{
const res = await User.create({
username,
phone,
password
})
//
return res.id
}
// 根据id查找某一用户
const findOneById=async (id)=>{
return await User.findById(id);
}
// 获取用户列表
const findAll=async ({current=1,pageSize=10,...other})=>{
// current 当前页码默认为1
// pageSize 一页数据条数
// other 其他查询参数
return await User.find(other) // 匹配特定条件的文档
.skip((current - 1) * pageSize) // 跳过前面的文档数量
.limit(pageSize) // 每页的文档数量
}
export defult{
create,
findOneById,
findAll
}
转载自:https://juejin.cn/post/7233984213515026491