设计耦合度更低的koa路由

Posted 冰空的作品展示

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计耦合度更低的koa路由相关的知识,希望对你有一定的参考价值。

设计耦合度更低的koa路由

平时用koa设计一个路由会怎么做呢? koa已经帮我们封装好了路由的大部分实现,而且用了async和await已经把异步回调进行了极大的简化,已经让写路由变得如此轻松。

 
   
   
 
  1. import Router from 'koa-router'

  2. import mongoose from 'mongoose'

  3. const router = new Router()

  4. router.get('/movies/all', async (ctx, next) => {

  5.    const Movie = mongoose.model('Movie')

  6.    try{

  7.        const movies = await Movie.find().sort({

  8.            'meta.createdAt' : -1

  9.        })

  10.        ctx.body = {

  11.            movies

  12.        }

  13.    } catch (e) {

  14.        console.log(e);

  15.    }

  16. })

这里面还进行了数据库的查询,然后返回数据api,看着还好,当中的数据处理什么的并不是很多,当以后要修改的时候感觉也挺好哒。对,毕竟自己开发的嘛? 这么说就这样好了,给你们看看前面写的像屎一样的博客后台的路由大概就会害怕了

想想看,一个博客路由这能有多少逻辑,一个路由模块居然就1200+行了。我也蛮不服的,但是事实就是这样,我把查询数据库,排序,数据处理等逻辑全写在这里面了,写的时候还蛮爽的,直到前面遇到几个问题之后我才觉得并没有那么简单,首先容易引起混乱,自己去找都不方便,接着就是改bug的时候简直等于把整个书签路由重构了。想想都操蛋诶!谁叫当时写的时候那么随意呢。

那有没有什么解决办法呢?不仅方便扩展,而且还可以满足开闭原则呢?答案当然不用说,设计模式登场。

因为正好js的装饰器是新的标准,并且大体上是照搬python的,那咱们就用这个装饰器模式来试试看。 首先我们来考虑下我们的需求,一般我们设计个后台api的时候,首先先是路由,有哪些路由,这些路由下又有些什么子路由,考虑好后我们就可以来设计啦。 既然用装饰器,我们可以先设计一个主路由的控制器来修饰这个类,子路由设计相应的控制器来修饰里面的方法,这样层次结构就很鲜明了。例如

 
   
   
 
  1. import { controller, post } from '../lib/decorator'

  2. import { checkPassword } from '../service/admin'

  3. @controller('/api/v0/user')

  4. export class User {

  5.    @post('/')

  6.    async loadControl (ctx, next) {

  7.        const { email, password } = ctx.request.body

  8.        const matchData = await checkPassword(email, password)

  9.        if(matchData.data) {

  10.            ctx.body = {

  11.                succsee: true

  12.            }

  13.        } else {

  14.            ctx.body = {

  15.                success: false,

  16.                errcode: '用户名或者密码不正确'

  17.            }

  18.        }

  19.    }

  20. }

其中 @get修饰,则说明以get方式获得, @post则以post方式提交。紧接着就看装饰器如何实现了。 主路由的控制器,主要作用就是取得主路由,并且把它放到原型上作为一个静态的属性供由类里面的进行调用。

 
   
   
 
  1. export const controller = path => target => (target.prototype[symbolPrefix] = path)

对方法修饰的装饰器就稍微麻烦一点点,这里我们得进行区分网络请求的方式,然后对不同的请求方式进行配置的存储,存储这里我们用到es6的map数据结构。例如

 
   
   
 
  1. const normalizePath = path => path.startsWith('/') ? path : `/${path}`

  2. const router = conf => (target, key, descriptor) => {

  3.    conf.path = normalizePath(conf.path)

  4.    console.log(...conf)

  5.    routerMap.set({

  6.        target: target,

  7.        ...conf

  8.    }, target[key])

  9. }

  10. export const get = path => router({

  11.    method: 'get',

  12.    path

  13. })

  14. export const post  = path => router({

  15.    method: 'post',

  16.    path

  17. })

  18. export const put = path => router({

  19.    method: 'put',

  20.    path

  21. })

  22. //...

这里我们把路由配置项都存起来,里面用请求方式和子路由内容为键,类里面的方法提取出来为值。 接着我们要做的就是初始化路由,我们新建一个路由类,里面建一个初始化路由的方法。这里咱们把每一个主路由的类进行加载,,因为装饰器对类的行为在编译时候就已经发生,而不在运行的时候。所以我们直接加载那几个类,就能拿到它们的方法以及装饰器发生完后存储的路由配置项。对配置项进行遍历,我们在初始化这里就能轻松的进行每一个路由拼接,加载,并开始侦听。

 
   
   
 
  1. export class Route {

  2.    constructor(app, apiPath){

  3.        this.app = app

  4.        this.apiPath = apiPath

  5.        this.router = new Router()

  6.    }

  7.    init(){

  8.        glob.sync(resolve(this.apiPath, './**/*.js')).forEach(require)

  9.        for(let [conf, controller] of routerMap) {

  10.            const controllers = isArray(controller)

  11.            let prefixPath = conf.target[symbolPrefix]

  12.            if(prefixPath) prefixPath = normalizePath(prefixPath)

  13.            const routerPath = prefixPath + conf.path

  14.            this.router[conf.method](routerPath, ...controllers)

  15.        }

  16.        this.app.use(this.router.routes())

  17.            .use(this.router.allowedMethods())

  18.    }

  19. }

这么一搞,猛地就能发现,这个模块可以直接不需要修改就可以在大部分场景中直接运用,而且路由模块进行了拆分。耦合度很低,可以随心所欲的加路由,对路由修改,对路由业务逻辑修改。

哈哈,没想到吧!装饰器居然这么好用,,(几乎和py的一毛一样)!

后面呢,好奇心驱使,估计会去折腾一段时间的函数式编程,那玩意和数学走的就更近了,貌似又是一段漫长的停笔期。好啦这次就水到这里!咱下次再见!

特别注意:由于es2017的装饰器就目前而言,浏览器中和node暂未实现,请使用Babel进行编译!


以上是关于设计耦合度更低的koa路由的主要内容,如果未能解决你的问题,请参考以下文章

面向过程程序设计,面向对象程序设计,可视化程序设计的异同

Koa答疑,你见过同步的Node.js代码么?

上传失败您需要使用版本号为29或更低的SDK

RecyclerView的使用

RecyclerView的使用

运放设计的十个大坑