一起来写个node服务,koa框架
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
koa--基于Node.js平台的下一代Web开发框架;这标语把我整的明明白白的,试试怎么玩?
看完本文,你也能用koa,实现一个基础的可用的Node服务了
当前环境
node -v v14.16.1
npm -v 6.14.12
起步
-
创建一个文件夹,我这里命名为koa-mock(因为我这个是用来写mock数据的),君且随意
-
进入文件夹,初始化一个
package.json
文件 -
安装koa,
npm install koa -S
-
创建一个src的目录,并在下面创建一个
index.js
文件 -
写个例子看看
const Koa = require('koa'); const app = new Koa(); app.use(async ctx => { ctx.body = 'Hello World,My first koa'; }); app.listen(3000); // 如果端口被占用了,请杀死进程或更换端口
-
终端运行
node src/index.js
在浏览器访问,http://localhost:3000/
,即可看到Hello World,My first koa
- 到这里第一阶段就顺利完成了,你也启动了一个node服务了,是不是很简单
挂挡
洋葱模型
Koa应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行(先进后出)
当一个中间件调用
next()
则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为。
看个例子
const Koa = require('koa');
const app = new Koa();
const middleware1 = function async(ctx, next) {
console.log('this is middleware1');
next()
}
const middleware2 = function async(ctx, next) {
console.log('this is middleware2');
next()
console.log('this is middleware2 end');
}
const middleware3 = function async(ctx, next) {
console.log('this is middleware3');
next()
console.log('this is middleware3 end');
}
app.use(middleware1)
app.use(middleware2)
app.use(middleware3)
app.listen(3000);
碰到next()
,我们就把控制器交给下一个中间件,直到下游没有更多中间件,我们就反向执行,所以打印的是
- this is middleware1
- this is middleware2
- this is middleware3
- this is middleware3 end
- this is middleware2 end
如图
这个例子非常重要,是koa最大的特点,中间件流程控制;Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行Response的逻辑,这里的request的逻辑,我们可以理解为是next之前的内容,response的逻辑是next函数之后的内容,也可以说每一个中间件都有两次处理时机。
koa路由
Koa 并没有捆绑任何中间件,所以我们使用的每一个中间件都需要自己安装;Web服务中,我们一般用URL来区分应该执行哪些方法,将数据返回给客服端,这个路径和执行函数的关系映射,就是路由,即构建一套关系映射,用来处理不同的请求。
-
安装koa路由中间件,
npm install koa-router -S
-
写代码
const koa = require('koa') const Router = require('koa-router') const app = new koa(); const router = new Router() // 设置路由前缀 router.prefix('/api') // 访问路由路径`/api/`的处理函数 router.get('/', ctx => { ctx.body = 'Hello World,My first koa router'; }) // 访问路由路径`/api/second`的处理函数 router.get('/second', ctx => { ctx.body = 'Hello World,My second koa router'; }) // 使用路由和路由方法 app.use(router.routes()) .use(router.allowedMethods) .listen(3000) // 将给定的中间件方法添加到此应用程序。app.use() 返回 this, 因此可以链式表达
-
分别访问
http://localhost:3000/api/
和http://localhost:3000/api/second
,分别输出Hello World,My first koa router
,Hello World,My second koa router
前面说了koa没有绑定任何中间件,那要实现一些功能,怎么知道它有没有对应的中间件呢,这个时候可以去百度或者去npm去找对应的中间件,本文也会把常用的中间件都用上一用
参数解析
koa-body 是一个可以帮助解析 http 中 body 的部分的中间件,包括 json、表单、文本、文件等
npm install koa-body -S
const { koaBody } = require('koa-body');
app.use(koaBody())
- 获取参数就可以使用
ctx.request.body
了,获取上传后文件的信息,则需要在ctx.request.files
中获取
这是最基本的使用,后面例子中再讲接收文件等静态资源时koa-body应该怎么配置,例如
app.use(koaBody({
multipart:true, // 支持文件上传
encoding:'gzip',
formidable:{
uploadDir:path.join(__dirname,'public/upload/'), // 设置文件上传目录
keepExtensions: true, // 保持文件的后缀
maxFieldsSize:2 * 1024 * 1024, // 文件上传大小
onFileBegin:(name,file) => { // 文件上传前的设置
},
}
}));
跨域处理
npm install @koa/cors -S
const cors = require('@koa/cors')
app.use(cors())
是不是特别简单,哈哈
安全头
npm install koa-helmet -S
const helmet = require('koa-helmet')
app.use(helmet())
静态资源
npm intall koa-static -S
const static = require('koa-static')
const path = require('path')
app.use(static(path.join(__dirname, '../public')))
输出json格式化
npm install koa-json -S
app.use(json({pretty: false, param: 'pretty'})) // 输出参数带pretty可以格式化成json
至此我们就有了这样一份代码index.js
,接下来我们进行合理的分配和文件整理
const koa = require('koa');
const Router = require('koa-router');
const static = require('koa-static');
const path = require('path');
const jsonutil = require('koa-json');
const cors = require('@koa/cors');
const helmet = require('koa-helmet');
const { koaBody } = require('koa-body');
const app = new koa();
const router = new Router();
// 设置路由前缀
router.prefix('/api')
// 访问路由路径`/api/`的处理函数
router.get('/', ctx => {
console.log(ctx)
console.log(ctx.request);
ctx.body = 'Hello World,My first koa router';
})
// 访问路由路径`/api/second`的处理函数
router.get('/second', ctx => {
console.log(ctx)
console.log(ctx.request);
ctx.body = 'Hello World,My second koa router';
})
// 使用路由中间件和路由方法
app.use(koaBody())
app.use(static(path.join(__dirname, '../public')))
app.use(cors())
app.use(jsonutil({ pretty: false, param: 'pretty' }),)
app.use(helmet())
app.use(router.routes())
app.use(router.allowedMethods)
app.listen(3000)
// 将给定的中间件方法添加到此应用程序。app.use() 返回 this, 因此可以链式表达
加速
ES6支持
都2022年了,肯定是要用ES6的,而使用ES6,需要借助Babel
进行编译;如果通过webpck
在lodaer中配置通过使用babel-loader
使用Babel;会更加美妙些
npm install webpack@4 webpack-cli@3 -D
// webpack4.0之后 webpack 和webpack-cli 分离了,所以需要安装两个依赖包
建立webpack.config.js文件,安装常用插件
npm install -D clean-webpack-plugin webpack-node-externals @babel/core @babel/node @babel/preset-env babel-loader cross-env
// 清理dist下面的文件、对node_modules下的文件做排除处理、babel相关(windows上@babel/node进行全局安装,否则后续会提示:‘babel-node’不是内部或外部命令)、设置环境变量
webpack.config.js
const path = require('path')
const nodeExternals = require('webpack-node-externals')
const {
CleanWebpackPlugin
} = require('clean-webpack-plugin')
const webpackconfig = {
target: "node",
mode: 'development',
entry: {
server: path.join(__dirname, './src/index.js')
},
output: {
filename: 'app.js',
path: path.join(__dirname, './dist')
},
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader'
},
exclude: [path.join(__dirname, '/node_modules')]
}]
},
externals: [nodeExternals()],
plugins: [
new CleanWebpackPlugin()
],
node: {
console: true,
global: true,
process: true,
Buffer: true,
__filename: true,
__dirname: true,
setImmediate: true,
path: true
}
}
module.exports = webpackconfig
建立.babelrc文件
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
接下来就是把之前导入的语法全部改出ES6导入写法
import koa from 'koa'
import Router from 'koa-router';
import statics from 'koa-static';
import path from 'path';
import jsonutil from 'koa-json';
import cors from '@koa/cors';
import helmet from 'koa-helmet';
import koaBody from 'koa-body';
使用npx babel-node src/index.js
进行编译就可以啦
路由合并
-
首先我们把路由和路由对应的实现方法分开了,在src下分别创建
controller
和routes
两个文件夹controller
下存放着路由对应的是实现方法routes
存放着各个模块的路由
-
在
routes
下创建一个modules
文件夹,用于存放区分各模块,假设我们现在有两个模块,一个用户模块、一个文章模块-
在
modules
下创建userRouter.js
;在controller
下创建userController.js
userRouter.js
,假设有两个路由,获取用户信息和修改密码
import Router from 'koa-router' import userController from '../../controller/userController' const router = new Router() router.prefix("/user") // 获取用户基本信息 router.get('/basicInfo', userController.getBasicInfo) // 获取用户基本信息 router.get('/changePassword', userController.changePasswd) export default router
userController.js
,就有对应的实现
class UserController { // 获取用户信息 async getBasicInfo(ctx) { ctx.body = { code: 200, data: {userName:'George'}, msg: '查询成功!' } } // 修改密码 async changePasswd(ctx) { ctx.body = { code: 200, msg: '修改成功!' } } } export default new UserController()
-
在
modules
下创建contenRouter.js
;在controller
下创建contenController.js
contenRouter.js
,假设有一个路由,发表文章
import Router from 'koa-router' import contentController from '../../controller/contenController' const router = new Router() router.prefix('/content') // 发表文章 router.post('/addPost', contentController.addPost) export default router
contenController.js
,就有对应的实现
class ContentController { async addPost(ctx) { ctx.body = { code: 200, msg: '添加文章成功!' } } } export default new ContentController()
-
-
这样我们就有了两个路由:用户路由和文章路由;可能之后还有很多路由,我们需要把这些路由进行集中,进行合并暴露出去,写在一起肯定是下下之选了,所以我们使用另一个中间件
koa-combine-routers
进行合并 -
在
routes
下创建一个route.js
,该文件进行合并路由,作为统一出口,npm install koa-combine-routers
import combineRoutes from 'koa-combine-routers' import userRouter from './modules/userRouter' import contenRouter from './modules/contenRouter' export default combineRoutes(userRouter, contenRouter)
-
修改index.js
import koa from 'koa' import router from './routes/route' import statics from 'koa-static'; import path from 'path'; import jsonutil from 'koa-json'; import cors from '@koa/cors'; import helmet from 'koa-helmet'; import koaBody from 'koa-body'; const app = new koa(); app.use(koaBody()) app.use(statics(path.join(__dirname, '../public'))) app.use(cors()) app.use(jsonutil({ pretty: false, param: 'pretty' }),) app.use(helmet()) app.use(router()) app.listen(3000)
热更新
不想每修改一点东西就重新启动一下,那就使用热更新,用nodemon
监听文件变化,自动重启
-
npm install -D nodemon
-
用npx执行
npx nodemon --exec babel-node src/index.js
-
或者修改pagejson的文件
"scripts": { "start:es6": "nodemon --exec babel-node src/index.js" },
npm run start:es6
整合koa中间件
-
npm install koa-compose -S
import koa from 'koa' import router from './routes/route.js' import path from 'path' import helmet from 'koa-helmet' import statics from 'koa-static' import koaBody from 'koa-body' import jsonutil from 'koa-json' import cors from '@koa/cors' import compose from 'koa-compose' const app = new koa(); const middleware = compose( [ koaBody({ multipart: true, encoding: 'utf-8', formidable: { keepExtensions: true, maxFieldsSize: 5 * 1024 * 1024 }, onError: err => { console.log('koabody Err', err); } }), statics(path.join(__dirname, '../public')), cors(), jsonutil({ pretty: false, param: 'pretty' }), helmet(), ] ) app.use(middleware) app.use(router()) app.listen(3000)
到这里为止,我们用koa已经实现了一个基础的可用的Node服务了,当我们访问localhost:3000/user/basicInfo
等接口时,能返回我们写的结果;但是这仅仅只是起步、挂挡、加速;我们还需要会其他很多技巧,换挡、倒车入库、侧方停车等等;不要着急,慢慢积累,多花时间练习,一定可以很好的掌握的。
继续行驶
- 怎么和数据库打交道
- 怎么加入鉴权机制
- 怎么实现websocket长链接
- 怎么实现邮件服务
- 怎么统一处理错误
- 怎么实现生产环境和开发环境的区分和支持
- ........
如果你看到这里了,烦请大佬点个赞,鼓励小弟学习,不胜感激,谢谢。
转载自:https://juejin.cn/post/7173969986099085326