Koa
导读
中间件、路由、参数解析、静态资源、错误处理、koa处理异步数据、express处理异步数据
创建服务器
const Koa = require('Koa') const app = new Koa() app.use((context,next)=>{ // context.status=200 // context.body="返回的内容" //有些可以直接使用某一个方法和属性,不需要response context.response.body="返回的内容" }) app.listen(8000,()=>{ console.log('启动成功') })
中间件
koa注册中间件只能通过use方法
- koa并没有提供methods的方式(get..)来注册中间件
- 没有提供path中间件来匹配路径(‘/login’)
- 也没有提供连续多个中间件的处理
- 只能在路由的方式里才能实现多个中间件的使用
koa的中间件的调用和express之间不一样
- 例如多个中间件中,express在匹配的所有中间件中,只要某一个中间件执行了res.send/res.json,那么后边匹配到的中间件就不会再执行了.
- 而koa会对ctx.body先进行保存,等把所有通过next()匹配到的中间件执行完后,再返回body客户端数据.即洋葱模型
- 而express只有在处理同步数据的时候才是洋葱模型
//koa没有提供这种方式 app.get('/',n,n2,(req,res)=>{ res.send() })
使用
const Koa = require('Koa') const app = new Koa() //use注册中间件 app.use((ctx,next)=>{ if(ctx.request.url === '/login'){ if(ctx.request.method === 'GET'){ ctx.response.body = '登录成功' } }else{ ctx.response.body = '先登录' } }) app.listen(8000,()=>{ console.log('启动成功') })
路由
在koa中需要使用第三方库来实现路由.
用户路由文件users.js
const Router = require('koa-router') //第三方库 const router = new Router({prefix:"/users"}) // '/users' router.get('/',(ctx,next)=>{ ctx.response.body="用户信息" }) // '/users/add' router.get('/add',(ctx,next)=>{ ctx.response.body="添加用户信息" }) module.exports = router
koa可以在路由中使用多个中间件
router.get('/',n1,n2,(ctx,next)=>{ ctx.response.body="返回的内容" })
使用路由app.js
const Koa = require('koa') const userRouter = require('./router/users') const app = new Koa() //use注册中间件 app.use((ctx,next)=>{ next() //转交给下一个中间件 }) //注册路由 app.use(userRouter.routes()) //作用:自动实现遇到程序没有注册的请求方式后,会返回给后端该方式不允许(Method Not Allowed,code:405) app.use(userRouter.allowedMethods()) app.listen(8000,()=>{ console.log('启动成功') })
allowedMethods()作用
自动实现遇到程序没有注册的请求方式后,会返回给后端该方式不允许(Method Not Allowed,code:405)
koa中的五种参数解析
我们通常通过query、params、json、x-www-form-urlencoded、form-body(包括它的文件上传)五种方式传递数据.
query和params的解析
const Koa = require('koa') const Router = require('koa-router') const userRouter = new Router({prefix:'/users'}) userRouter.get('/:id',(ctx,next)=>{ console.log('query',ctx.request.query) console.log('params',ctx.request.params) //params必须配合路由使用,因为这样才知道哪些是路径,哪些是参数 }) const app = new Koa() app.use(userRouter.routes()) app.listen(8000,()=>{ console.log('启动成功') }) // query [Object: null prototype] { name: 'wxl' } // params { id: '100' }
json和x-www-form-urlencoded的解析
在koa里需要使用第三方库koa-bodyparser来解析这两种body,而且该库只要放在最前面,就可以同时解析这两种格式,不再需要指定了.
const Koa = require('koa') const bodyParser = require('koa-bodyparser') const app = new Koa() app.use(bodyParser()) //要放在最前面,解析json和x-www-form-urlencoded app.use((ctx,next)=>{ console.log(ctx.request.body) //此时就可以拿到解析后的body数据了 ctx.response.body="返回的内容" }) app.listen(8000,()=>{ console.log('启动成功') }) //{ name: 'wxl' } //{ name: 'zzl' }
form-body的解析
koa里需要第三方库koa-multer来解析
const Koa = require('koa') const multer = require('koa-multer') const Router = require('koa-router') const userRouter = new Router({prefix:'/users'}) const upload = multer() //只能在路由的方式里才能使用多个中间件的使用 userRouter.post('/add',upload.any(),(ctx,next)=>{ console.log(ctx.req.body) ctx.response.body="返回的内容" }) const app = new Koa() app.use(userRouter.routes()) app.listen(8000,()=>{ console.log('启动成功') }) // [Object: null prototype] { name: 'wxl' }
文件上传
const Koa = require('koa') const multer = require('koa-multer') const Router = require('koa-router') const uploadRouter = new Router({prefix:'/upload'}) const storage = multer.diskStorage({ destination:(req,file,cb)=>{ cb(null,'./imgs') //存储的位置 }, filename:(req,file,cb)=>{ cb(null,Math.random()+'diy_name.png') //存储的文件名 } }) const upload = multer({ // dest:'./imgs' 存储的位置(简单配置) storage:storage //复杂配置 }) //只能在路由的方式里才能使用多个中间件的使用 uploadRouter.post('/img',upload.single('file_key_name'),(ctx,next)=>{ console.log(ctx.req.file) ctx.response.body="上传成功" }) const app = new Koa() app.use(uploadRouter.routes()) app.listen(8000,()=>{ console.log('启动成功') }) /* { fieldname: 'file_key_name', originalname: '*O2022-11-29 20.17.29.png', encoding: '7bit', mimetype: 'image/png', destination: './imgs', filename: '0.2637499180920484diy_name.png', path: 'imgs/0.2637499180920484diy_name.png', size: 345631 } */
静态资源
koa需要第三方库koa-static来使用静态资源
const Koa = require('Koa') const staticAssets = require('koa-static') const app = new Koa() app.use(staticAssets('./public')) app.use((context,next)=>{ }) app.listen(8000,()=>{ console.log('启动成功') })
错误处理
koa的错误处理和express类似
const Koa = require('Koa') const app = new Koa() app.use((ctx,next)=>{ const isLogin = false if(!isLogin){ ctx.app.emit('myError',new Error('not_login'),ctx) } }) app.on('myError',(err,ctx)=>{ let status='' let message='' switch(err.message){ case 'not_login': message="not login!" status=401 break default: message="null" status=400 break } ctx.status=status ctx.body=message }) app.listen(8000,()=>{ console.log('启动成功') })
koa处理异步数据
对于express,它只能处理同步数据,对于一部分数据不太好处理,而koa是容易处理异步数据的,因为koa种的 next()返回的是一个promise,所以我们可以使用await来等待异步数据的结果,供下边代码使用.
比如说对于匹配的中间件里有异步操作时,我们需要在转交下一个中间件的时候使用await,这样才能保证能得到所有被处理过的结果.例如:
onst verifyUser = async (ctx, next) => { // 1.获取用户名和密码 const { name, password } = ctx.request.body; // 2.判断用户名或者密码不能空 if (!name || !password) { const error = new Error(errorTypes.NAME_OR_PASSWORD_IS_REQUIRED); return ctx.app.emit('error', error, ctx); } // 3.判断这次注册的用户名是没有被注册过 const result = await service.getUserByName(name); if (result.length) { const error = new Error(errorTypes.USER_ALREADY_EXISTS); return ctx.app.emit('error', error, ctx); } await next(); } //因为第二个匹配的中间件是异步的 const handlePassword = async (ctx, next) => { const { password } = ctx.request.body; ctx.request.body.password = md5password(password) await next(); } userRouter.post('/login',verifyUser,handlePassword)