设计耦合度更低的koa路由
Posted 冰空的作品展示
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计耦合度更低的koa路由相关的知识,希望对你有一定的参考价值。
设计耦合度更低的koa路由
平时用koa设计一个路由会怎么做呢? koa已经帮我们封装好了路由的大部分实现,而且用了async和await已经把异步回调进行了极大的简化,已经让写路由变得如此轻松。
import Router from 'koa-router'
import mongoose from 'mongoose'
const router = new Router()
router.get('/movies/all', async (ctx, next) => {
const Movie = mongoose.model('Movie')
try{
const movies = await Movie.find().sort({
'meta.createdAt' : -1
})
ctx.body = {
movies
}
} catch (e) {
console.log(e);
}
})
这里面还进行了数据库的查询,然后返回数据api,看着还好,当中的数据处理什么的并不是很多,当以后要修改的时候感觉也挺好哒。对,毕竟自己开发的嘛? 这么说就这样好了,给你们看看前面写的像屎一样的博客后台的路由大概就会害怕了
想想看,一个博客路由这能有多少逻辑,一个路由模块居然就1200+行了。我也蛮不服的,但是事实就是这样,我把查询数据库,排序,数据处理等逻辑全写在这里面了,写的时候还蛮爽的,直到前面遇到几个问题之后我才觉得并没有那么简单,首先容易引起混乱,自己去找都不方便,接着就是改bug的时候简直等于把整个书签路由重构了。想想都操蛋诶!谁叫当时写的时候那么随意呢。
那有没有什么解决办法呢?不仅方便扩展,而且还可以满足开闭原则呢?答案当然不用说,设计模式登场。
因为正好js的装饰器是新的标准,并且大体上是照搬python的,那咱们就用这个装饰器模式来试试看。 首先我们来考虑下我们的需求,一般我们设计个后台api的时候,首先先是路由,有哪些路由,这些路由下又有些什么子路由,考虑好后我们就可以来设计啦。 既然用装饰器,我们可以先设计一个主路由的控制器来修饰这个类,子路由设计相应的控制器来修饰里面的方法,这样层次结构就很鲜明了。例如
import { controller, post } from '../lib/decorator'
import { checkPassword } from '../service/admin'
@controller('/api/v0/user')
export class User {
@post('/')
async loadControl (ctx, next) {
const { email, password } = ctx.request.body
const matchData = await checkPassword(email, password)
if(matchData.data) {
ctx.body = {
succsee: true
}
} else {
ctx.body = {
success: false,
errcode: '用户名或者密码不正确'
}
}
}
}
其中 @get
修饰,则说明以get方式获得, @post
则以post方式提交。紧接着就看装饰器如何实现了。 主路由的控制器,主要作用就是取得主路由,并且把它放到原型上作为一个静态的属性供由类里面的进行调用。
export const controller = path => target => (target.prototype[symbolPrefix] = path)
对方法修饰的装饰器就稍微麻烦一点点,这里我们得进行区分网络请求的方式,然后对不同的请求方式进行配置的存储,存储这里我们用到es6的map数据结构。例如
const normalizePath = path => path.startsWith('/') ? path : `/${path}`
const router = conf => (target, key, descriptor) => {
conf.path = normalizePath(conf.path)
console.log(...conf)
routerMap.set({
target: target,
...conf
}, target[key])
}
export const get = path => router({
method: 'get',
path
})
export const post = path => router({
method: 'post',
path
})
export const put = path => router({
method: 'put',
path
})
//...
这里我们把路由配置项都存起来,里面用请求方式和子路由内容为键,类里面的方法提取出来为值。 接着我们要做的就是初始化路由,我们新建一个路由类,里面建一个初始化路由的方法。这里咱们把每一个主路由的类进行加载,,因为装饰器对类的行为在编译时候就已经发生,而不在运行的时候。所以我们直接加载那几个类,就能拿到它们的方法以及装饰器发生完后存储的路由配置项。对配置项进行遍历,我们在初始化这里就能轻松的进行每一个路由拼接,加载,并开始侦听。
export class Route {
constructor(app, apiPath){
this.app = app
this.apiPath = apiPath
this.router = new Router()
}
init(){
glob.sync(resolve(this.apiPath, './**/*.js')).forEach(require)
for(let [conf, controller] of routerMap) {
const controllers = isArray(controller)
let prefixPath = conf.target[symbolPrefix]
if(prefixPath) prefixPath = normalizePath(prefixPath)
const routerPath = prefixPath + conf.path
this.router[conf.method](routerPath, ...controllers)
}
this.app.use(this.router.routes())
.use(this.router.allowedMethods())
}
}
这么一搞,猛地就能发现,这个模块可以直接不需要修改就可以在大部分场景中直接运用,而且路由模块进行了拆分。耦合度很低,可以随心所欲的加路由,对路由修改,对路由业务逻辑修改。
哈哈,没想到吧!装饰器居然这么好用,,(几乎和py的一毛一样)!
后面呢,好奇心驱使,估计会去折腾一段时间的函数式编程,那玩意和数学走的就更近了,貌似又是一段漫长的停笔期。好啦这次就水到这里!咱下次再见!
特别注意:由于es2017的装饰器就目前而言,浏览器中和node暂未实现,请使用Babel进行编译!
以上是关于设计耦合度更低的koa路由的主要内容,如果未能解决你的问题,请参考以下文章