网站开发工具的功能包括HTML或,备案时的网站建设方案书,互联网企业排名2021,专业做网站技术文章目录 概述koa和express对比koa下载安装使用1.创建koa项目文件目录2. 创建koa服务3. 添加路由 koa-router4. 数据库服务 mongodb5. 添加请求参数json处理 koa-bodyparser6. 用户接口举例7.引入koa一些常用插件8.用户登录验证 koa-jwt9.webpack生产打包 来源 概述
Koa 是一个… 文章目录 概述koa和express对比koa下载安装使用1.创建koa项目文件目录2. 创建koa服务3. 添加路由 koa-router4. 数据库服务 mongodb5. 添加请求参数json处理 koa-bodyparser6. 用户接口举例7.引入koa一些常用插件8.用户登录验证 koa-jwt9.webpack生产打包 来源 概述
Koa 是一个新的 web 框架由 Express 幕后的原班人马打造 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数Koa 帮你丢弃回调函数并有力地增强错误处理。 Koa 并没有捆绑任何中间件 而是提供了一套优雅的方法帮助您快速而愉快地编写服务端应用程序。
koa和express对比
Koa采用洋葱模型
通常都会说Koa是洋葱模型,这重点在于中间件的设计。但是按照上面的分析,会发现Express也是类似的不同的是Express中间件机制使用了Callback 实现这样如果出现异步则可能会使你在执行顺序上感到困惑因此如果我们想做接口耗时统计、错误处理Koa的这种中间件模式处理起来更方便些。最后一点响应机制也很重要, Koa不是立即响应是整个中间件处理完成在最外层进行了响应而Express则是立即响应。
Koa更轻量
koa不提供内置的中间件; koa不提供路由而是把路由这个库分离出来了(koa/router)
Context对象
koa增加了一个Context的对象作为这次请求的上下文对象(在koa2中作为中间件的第一个参数传入)。同时Context上也挂载了Request和Response两个对象。Express类似, 这两个对象都提供了大量的便捷方法辅助开发这样的话对于在保存一些公有的参 数的话变得更加合情合理。
异步流程控制
express采用callback来处理异步koa采用async/await。 async/await使用同步的写法来处理异步明显好于callback和promise,
中间件模型
express基于connect中间件,线性模型; koa中间件采用洋葱模型(对于每个中间件,在完成了-些事情后可以非常优雅的将控制权传递给下一个中间件并能够等待它完成当后续的中间件完成处理后控制权又回到了自己) 同步代码 同步方法没有什么区别
01-express-同步.js
const express require(express)
const app express()app.use((req, res, next) {console.log(111111)next()console.log(333333)res.send(hello world)
})app.use((req, res, next) {// 同步操作console.log(22222)
})app.listen(3000)运行输出
111111
22222
33333301-koa-同步 .js
const Koa require(koa)
const app new Koa()app.use((ctx, next) {console.log(111111)next()console.log(333333)ctx.body(hello world)
})app.use((ctx, next) {// 同步操作console.log(22222)
})app.listen(3000)运行输出
111111
22222
333333异步代码 next()表示可以执行下一个中间件当下一个中间件执行完成之后如果上一个中间件没有执行完再返回上一个中间件继续执行。
01-express-异步.js
const express require(express)
const app express()app.use(async (req, res, next) {console.log(111111)await next()console.log(444444)res.send(hello world)
})app.use(async (req, res, next) {console.log(22222)// 异步操作await delay(1000)console.log(33333)
})function delay(time) {return new Promise((resolve, reject) {setTimeout(resolve,time)})
}app.listen(3000)运行输出
111111
22222
444444
33333由于next()返回的不是promise对象因此await不起作用所以输出不会像我们所想输出
01-koa-异步.js
const Koa require(koa)
const app new Koa()app.use((ctx, next) {console.log(111111)next()console.log(444444)
})app.use((ctx, next) {console.log(22222)// 异步操作delay(1000)console.log(33333)
})function delay(time) {return new Promise((resolve, reject) {setTimeout(resolve,time)})
}
app.listen(3000)运行输出
111111
22222
33333
444444koa洋葱模型正常执行。
koa下载安装
npm init
npm i koaKoa基本框架
const Koa require(koa)const app new Koa()// ctxcontext 相当于res和req的合并
app.use((ctx, next) {})app.listen(3000)使用
1.创建koa项目文件目录
我们学习的第一步就是先搭好项目目录这里会有我们项目中使用到的任何东西。
// 创建项目文件夹 也可手动创建
mkdir koa-app// 进入项目文件
cd koa-app// 添加koa依赖
// 根据自己的包管理器执行自己的命令 我这里以yarn举例
yarn add koa -S// 新建入口文件
echo index.js// 创建变量管理目录 constmkdir const// 创建数据库管理目录 databasemkdir database// 创建中间件管理目录 middlewaresmkdir middlewares// 创建路由管理目录 routermkdir router// 创建静态资源管理目录 staticmkdir static// 创建工具类管理目录 utilsmkdir utils// 创建html文件管理目录 view
// 主要用于测试自己接口
mkdir const// 创建webpack打包文件echo webpack.config.js这时候我们生成的目录结构大致如下
--koa-app--const--database--middlewares--router--static--utils--view-- index.js-- packjson.js-- webpack.config.js2. 创建koa服务
这时候我们就可以创建koa服务启动后就可以访问服务器目录了。
// index.jsconst Koa require(koa);const app new Koa();app.use(async ctx {ctx.body hellO 欢迎使用koa
})app.listen(3000);// 启动koa服务node index.js// 访问服务在浏览器地址栏输入 localhost:3000启动服务后我们打开浏览器就能看到hellO 欢迎使用koa说明我们koa程序运行成功。
3. 添加路由 koa-router
我们后台服务已经搭建好了那下一步必不可少的就是路由管理了这里我们使用koa-router插件
// /router/index.js// 引入koa-router
const Router require(koa-router);
// 引入user路由对象
const user require(./user/index.js);
const view require(./view/index.js)
const goods require(./goods/index.js);
const category require(./category/index.js);
const upload require(./upload/index.js)
const rule require(./rule/index.js)
const menu require(./menu/index.js)
const role require(./role/index.js)
const managerUser require(./managerUser/index.js)
const attribute require(./attribute/index.js)// 生成新的router对象
let router new Router();// 添加路由管理
router.use(/api/user, user.routes())
router.use(/view, view.routes())
router.use(/api/goods, goods.routes())
router.use(/api/category, category.routes())
router.use(/api/upload, upload.routes())
router.use(/api/rule, rule.routes())
router.use(/api/menu, menu.routes())
router.use(/api/role, role.routes())
router.use(/api/managerUser, managerUser.routes())
router.use(/api/attribute, attribute.routes())// 导出路由
module.exports router这里我是以自己写好的项目文件直接复制了如果是测试的话不需要导入那么多路由对象导入一个自己已经写好的就行了。
接下来我们就需要修改index.js文件与编写user路由
// /router/user/index.jsrouter.get(/list, async (ctx) {ctx.body {code: 200,message: 访问成功}
})// index.jsconst Koa require(koa);
const router require(./router/index.js); // 路由
const app new Koa();// 添加路由中间件
app.use(router.routes()).use(router.allowedMethods());
app.use(async ctx {ctx.body hellO 欢迎使用koa
})app.listen(3000);这时候我们重新启动koa服务后访问localhost/3000/api/user/list 就能获取ctx.body的内容了。 做到这里我们已经实现了自己的第一个接口了。剩下就是去数据库里面获取数据就形成了后台数据服务了。 是不是很棒呢
4. 数据库服务 mongodb
这里因为学习的是mongodb数据库所以例子都会是以mongodb数据库为例。其实用mysql的同学也可以自己去看一下mysql的引。
数据库的引入主要是做了2个步骤 第一连接数据库第二创建数据model对象并执行数据库操作。 mongodb使用的是mdb语句去做的查询mysql则是使用的sql语句。
当然每个数据库特性都不一样在什么项目中使用什么数据库都需要在搭建项目目录的时候考虑到的比如mysql, oracle 都是关系型的在做一些数据关联性强的一些网站上更加适用比如电商金融证券医疗等。 而非关系型的mongodb数据因为数据结构更加多变适用与一些日记管理博客官网等
话不多说我们来创建我们的数据库服务吧
添加依赖
// 添加依赖
yarn add glob mongoose -S创建mongosse文件
// 添加mongoose文件 /database/index.js
// 添加mongosse
const mongoose require(mongoose)
// 数据库访问地址
const db mongodb://127.0.0.1/waimai
// glob 提供匹配文件路径的方法可以快速地找 到需要读取的文件
const glob require(glob);
const { resolve } require(path)// 初始化文档模式
exports.initSchemas async () {await glob.sync(resolve(__dirname, ./schema, ./*.js)).forEach((v) {require(v)})
}exports.connect () {// 连接数据库mongoose.connect(db)return new Promise((resolve, reject) {// 添加数据库断开监听事件mongoose.connection.on(disconnected, () {console.log(数据库断开---------------)mongoose.connect(db)})// 添加数据库启动监听事件mongoose.connection.on(open, () {console.log(数据库连接---------------1)mongoose.connect(db)resolve();})})
}// index.js 引入mongoose文件// moogose初始化
const { connect, initSchemas } require(./database/index);(async () {await connect();await initSchemas();
})();我们重启服务后就能连接到mongodb数据库了 在conosle里面我们能看到 数据库连接字样
5. 添加请求参数json处理 koa-bodyparser
添加新依赖
yarn add koa-bodyparser -D更新index.js
const bodyParser require(koa-bodyparser); // requeast请求app.use(bodyParser());6. 用户接口举例
添加新依赖
yarn add bcrypt -D创建mongoose.model模型
const mongoose require(mongoose);
const Schema mongoose.Schema;
let ObjectId Schema.Types.ObjectId;
// const bcrypt require(bcrypt);const SALT_WORK_FACTOR 10;// 前台用户表接口
const userSchema new Schema({UserId: ObjectId,userName: {unique: true,type: String},passWord: String,avator: String,hashPassword: String,nikeName: String,address: String,isBlack: Boolean,sex: String,createAt: {type: Date,default: Date.now(),},lastLoginAt: {type: Date,default: Date.now(),},
})// 每次存储时都要执行加盐加密
userSchema.pre(save, function (next){bcrypt.genSalt(SALT_WORK_FACTOR,(err,salt){if(err) return next(err)bcrypt.hash(this.passWord,salt,(err,hash){if(err) return next(err)this.hashPassword hashnext()})})
})// 添加自定义方法
userSchema.methods {// 对比密码一致性comparePassword: (_password, hashPassword) {return new Promise((resolve, reject) {// 对比密码方法bcrypt.compare(_password, hashPassword, (err, isMatch) {if(!err) resolve(isMatch);reject(err)})}) }
}
// 发布模型
module.exports mongoose.model(User, userSchema)添加用户接口
const Router require(koa-router);
const mongoose require(mongoose)
let router new Router();
const User require(../../database/schema/User)// 用户注册
router.post(/register, async (ctx) {const userName ctx.request.body.userName;const passWord ctx.request.body.passWord;let newUser new User({userName,passWord,}).save().then((res) {ctx.body {code: 200,message: 添加用户成功,};}).catch((err) {ctx.body {code: 500,message: 添加失败 err,};});;
})router.get(/list, async (ctx) {// 引入user模型// const User mongoose.model(User);const uid ctx.request.query.uid || ;// 分页 pagelet page ctx.request.body.page || 1;// 分页每页数量let limit ctx.request.body.limit || 8;// 上一次获取位置const start (page - 1)*limit;// console.log( userName, User, User)const result await User.find().exec()console.log(result, result)ctx.body {code: 200,data: result.slice((page-1)*limit, page*limit),page: {page: page,limit,total: result.length ,lastPage: parseInt(result.length / limit)}}// ctx.body ctx.request.body;
})module.exports router;这时候我们就已经写好了用户添加接口与用户列表接口。因为用户的密码需要保密我们在这里用了bcrypt去做了加盐加密考虑到了bcrypt的加密是不可逆的所以我们这里用了passWord对原密码做了保存。
这里我们使用了schema的自定义方法与 schema的钩子函数
userSchema.methods
向由该 schema 编译的 model 构造的 document 添加一个实例方法.
userSchema.pre
给 schema 定义一个前置钩子 (pre hook)
7.引入koa一些常用插件
处理跨域问题 koa2-cors
yarn add koa2-cors -D// index,js
const koa2cors require(koa2-cors); // 配置跨域app.use(koa2cors({origin: *,maxAge: 5,allowMethods: [GET, POST, PUT, DELETE, OPTIONS],allowHeaders: [Content-Type, Authorization, Accept],
}));添加静态文件目录 koa-static
yarn add koa-static -D// index.jsconst koaStatic require(koa-static); // 静态目录
app.use(koaStatic(./));添加websokit服务 koa-websocket
yanr add koa-websocket -S// index.jsconst websocket require(koa-websocket); // socket// 这时候app需用被websoket包裹
const app websocket(new Koa());// 建立socket连接
app.ws.use(async (ctx) {// the websocket is added to the context as ctx.websocket.ctx.websocket.send(我是服务器);ctx.websocket.on(message, function (message) {// do somethingconst msg message.toString(utf-8);console.log(客户端发来消息, msg);});
});添加xss防御
yarn add xss -S// index.js
const xss require(./middlewares/xss.js) // xssapp.use(xss())// /middlewares/xss.jsconst xss require(xss); // 需要 npm install xss -Sconst xssHandler () {return async (ctx, next) {try {const body ctx.request.body;for (const key in body) {if (typeof body[key] string) {body[key] xss(body[key]);}}// 一定要添加awaitawait next();} catch (error) {// console.error(error)throw error;}};
};module.exports xssHandler;图片文件处理 koa-multer
yarn add koa-multer -D// /router/upload/index.js
const Router require(koa-router);
let router new Router();
const multer require(koa-multer);//配置
const storage multer.diskStorage({//配置图片上传的目录destination: function (req, file, cb) {console.log(destination)cb(null, static/images/); //注意路径必须存在},//图片上传完成重命名filename: function (req, file, cb) {console.log(filename)// 获取后缀名var fileFormat file.originalname.split(.);cb(null, Date.now() . fileFormat[fileFormat.length - 1]);},
});
const upload multer({ storage: storage });
router.post(/img, upload.single(file), async ctx {console.log(ctx.req.file, ctx.req.file)ctx.body {code: 200, data: {filename: ctx.req.file.filename,//返回文件名 path: ctx.req.file.destination ctx.req.file.filename}}
})module.exports router;请求参数验证 Joi
访问接口时会先校验参数是否传对如果对继续后面的逻辑如果参数校验不对则会直接返回错误信息给前端。
yarn add Joi -D// /router/user/index.jsconst Joi require(joi);
const validateSchemaJoi require(../../middlewares/validateSchemaJoi);
const userSchema Joi.object({userName: Joi.string().min(1).required(),
});// 用户注册
router.post(/register, validateSchemaJoi(post, userSchema), async (ctx) {// 注册流程
})// /middlewares/validateSchemaJoi
function validateSchemaJoi(method, schema) {async function validateSchema (ctx, next) {let data undefined;if (method get) {data ctx.request.query;} else {data ctx.request.body;}const { value, error } schema.validate(data);if (error) {ctx.body {code: 400,error};} else {next();}}return validateSchema;}module.exports validateSchemaJoi;8.用户登录验证 koa-jwt
用户验证有3种方式
1。cookie 2. session 3. token
这里我们就以token来做用户验证。
添加依赖
yarn add koa-jwt jsonwebtoken -S用户登录添加token返回
// /router/user/index.jsvar jwt require(jsonwebtoken);router.post(/login, async (ctx) {const userName ctx.request.body.userName;const passWord ctx.request.body.passWord;// 查询用户是否存在await User.findOne({ userName: userName }).exec().then(async result {// 如果用户名存在if(result) {let newUser new User();// 校验用户密码await newUser.comparePassword(passWord, result.hashPassword).then(isMatch {// 如果用户校验成功if(isMatch) {// 生成tokenconst token jwt.sign({userName: result.userName}, secret, { expiresIn: 2h });// 返回给前端ctx.body {code: 200,message: isMatch,data: {token: token,uid: result._id}}}else {ctx.body {code: 500,message: isMatch}}})}else {ctx.body {code: 500,message: 用户名不存在}}}).catch(err {// console.log(result----err)ctx.body {code: 500,message: err,}})})添加token白名单 不拦截请求
const { jwtWhiteList } require(./const/jwtWhiteList); // token白名单// /const/jwtWhiteList.jsconst jwtWhiteList [/^\/api\/user\/login/,/^\/view/,/^\/static/,/api/managerUser/login,/api/goods/getGoodsDetailsInfo,/api/upload/img
]
module.exports {jwtWhiteList
}添加路由token验证拦截
token路由拦截主要做了以下这几件事
1.koa-jwt对每个请求头部信息进行token校验如果用户校验失败就返回401过滤掉白名单的请求。
2.在路由中间件上面添加中间件当用户token失效后我们就会走401步骤 返回用户token失效信息让前端去重定向到登录页
3.用户token都是有时效性的当然时效性越短越好因为没用数据库去存储token所以在项目重启后可能会有失效问题没验证过。我这默认是2小时当小于一半的失效时间时我就会生成新的token交予前端重新生成。也就是所谓的token续存机制。
// index.jsconst jwt require(koa-jwt); // token验证
const jwtToken require(jsonwebtoken);// 路由拦截器中间件
app.use(function (ctx, next) {// console.log(ce0, ctx.header.authorization)if (ctx.header ctx.header.authorization) {const parts ctx.header.authorization.split( );if (parts.length 2) {//取出tokenconst scheme parts[0];const token parts[1];if (/^Bearer$/i.test(scheme)) {try {const decoded jwtToken.verify(token, secret,{ complete: true });// iat: 签发时间 exp: 过期时间const { iat, exp, userName } decoded.payload;const nowTime new Date().getTime()/1000;const lastTime (exp - nowTime)/60;// 当前事件离过期时间还剩一半的时候更新token 如果过期就走401if(decoded 0 lastTime lastTime ((exp-iat)/60)/2) {// console.log(更新token0)const newToken jwtToken.sign({userName: userName}, secret, { expiresIn: 2h });// console.log(更新token1, newToken)ctx.res.setHeader(Authorization, newToken)}} catch (error) {console.log(ce3)//token过期 }}}}return next().catch((err) {if (401 err.status || err.status 301) {ctx.status 401;ctx.body {code: err.status,message: token已经失效};// ctx.body {error: err.originalError ? err.originalError.message : err.message};} else {throw err;}});
});// 添加token中间件
app.use(jwt({ secret: secret }).unless({ path: jwtWhiteList }));9.webpack生产打包
这里就做了简单的js打包打包后的文件体积会变小因为webpack设置mode为生产环境后默认就做了许多处理。
// webpack.config.js
const webpack require(webpack);const path require(path);const { CleanWebpackPlugin } require(clean-webpack-plugin);const nodeExternals require(webpack-node-externals);// const MinifyPlugin require(babel-minify-webpack-plugin);const CopyWebpackPlugin require(copy-webpack-plugin)module.exports {entry: ./index.js,mode: production,output: {path: path.resolve(__dirname, ./dist),filename: [name].js,},target: node,externals: [nodeExternals()], //node 打包可去除一些警告module: {rules: [{test: /\.js$/,exclude: /node_modules/,use: [{loader: babel-loader,options: {presets: [babel/preset-env], //兼容es6并添加.babelrc},},],},],},plugins: [// 清楚distnew CleanWebpackPlugin(),// js压缩// split切片// 复制静态目录new CopyWebpackPlugin({patterns: [{from: path.resolve(__dirname, ./static),to: path.resolve(__dirname, ./dist/static)}]})// new MinifyPlugin() //压缩js],};// packjson.js 添加启动指令build: webpack --progress --config webpack.config.js,
prd_server: node ./dist/main.js来源
你需要的koa入门教学 koa框架