在Spring中Controller长这样

  1. @Controller
  2. public class HelloController{
  3. @RequestMapping("/hello")
  4. String hello() {
  5. return "Hello World";
  6. }
  7. }

还有Python上的Flask框架

  1. @app.route("/hello")
  2. def hello():
  3. return "Hello World"

两者都用decorator来控制路由,这样写的好处是更简洁、更优雅、更清晰。

反观Express或Koa上的路由

  1. router.get('/hello', async ctx => {
  2. ctx.body = 'Hello World'
  3. })

完全差了一个档次

JS从ES6开始就有Decorator了,只是浏览器和Node都还没有支持。需要用babel-plugin-transform-decorators-legacy转义。

Decorator基本原理

首先需要明确两个概念:

  1. Decorator只能作用于类或类的方法上
  2. 如果一个类和类的方法都是用了Decorator,类方法的Decorator优先于类的Decorator执行

Decorator基本原理:

  1. @Controller
  2. class Hello{
  3. }
  4. // 等同于
  5. Controller(Hello)

Controller是个普通函数,target为修饰的类或方法

  1. // Decorator不传参
  2. function Controller(target) {
  3. }
  4. // Decorator传参
  5. function Controller(params) {
  6. return function (target) {
  7. }
  8. }

如果Decorator是传参的,即使params有默认值,在调用时必须带上括号,即:

  1. @Controller()
  2. class Hello{
  3. }

如何在Koa中使用Decorator

我们可以对koa-router中间件进行包装

先回顾一下koa-router基本使用方法:

  1. var Koa = require('koa');
  2. var Router = require('koa-router');
  3. var app = new Koa();
  4. var router = new Router();
  5. router.get('/', async (ctx, next) => {
  6. // ctx.router available
  7. });
  8. app
  9. .use(router.routes())
  10. .use(router.allowedMethods());

再想象一下最终目标

  1. @Controller({prefix: '/hello'})
  2. class HelloController{
  3. @Request({url: '/', method: RequestMethod.GET})
  4. async hello(ctx) {
  5. ctx.body = 'Hello World'
  6. }
  7. }

类内部方法的装饰器是优先执行的,我们需要对方法重新定义

  1. function Request({url, method}) {
  2. return function (target, name, descriptor) {
  3. let fn = descriptor.value
  4. descriptor.value = (router) => {
  5. router[method](url, async(ctx, next) => {
  6. await fn(ctx, next)
  7. })
  8. }
  9. }
  10. }

对RequestMethod进行格式统一

  1. const RequestMethod = {
  2. GET: 'get',
  3. POST: 'post',
  4. PUT: 'put',
  5. DELETE: 'delete'
  6. }

Controller装饰器需将Request方法添加到Router实例并返回Router实例

  1. import KoaRouter from 'koa-router'
  2. function Controller({prefix}) {
  3. let router = new KoaRouter()
  4. if (prefix) {
  5. router.prefix(prefix)
  6. }
  7. return function (target) {
  8. let reqList = Object.getOwnPropertyDescriptors(target.prototype)
  9. for (let v in reqList) {
  10. // 排除类的构造方法
  11. if (v !== 'constructor') {
  12. let fn = reqList[v].value
  13. fn(router)
  14. }
  15. }
  16. return router
  17. }
  18. }

至此,装饰器基本功能就完成了,基本使用方法为:

  1. import {Controller, Request, RequestMethod} from './decorator'
  2. @Controller({prefix: '/hello'})
  3. export default class HelloController{
  4. @Request({url: '/', method: RequestMethod.GET})
  5. async hello(ctx) {
  6. ctx.body = 'Hello World'
  7. }
  8. }

在App实例中同路由一样use即可。

原文地址:用Decorator控制Koa路由

我的博客:Bougie的博客

用Decorator控制Koa路由的更多相关文章

  1. koa 路由配置

    Koa 路由 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET.POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问. 通俗的讲:路由就是根据不 ...

  2. 使用Js控制ReactRouter路由

    [使用Js控制ReactRouter路由] 首先引入PropTypes: const PropTypes = require('prop-types'); 然后定义context的router属性: ...

  3. koa 路由、视图模块化(二)

    1.项目目录 2.路由 根目录/routes/index.js -- 首页 const router = require('koa-router')(); router.get('/', async ...

  4. koa 路由模块化(一)

    1.项目目录 2.入口文件 根目录/app.js /** * koa 路由模块化 */ const Koa = require('koa'); const router = require('koa- ...

  5. HCNP Routing&Switching之路由控制、路由策略和IP-Prefix List

    前文我们了解了IS-IS路由聚合和认证相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15306645.html:今天我们来聊一聊路由控制技术中的路由策 ...

  6. Arduino IDE for ESP8266 (4)局域网 网页图形化控制灯 路由系统

    用到的路由系统文件(备用) 链接:https://pan.baidu.com/s/1bqR7Lc7 密码:7w2z 教程http://www.windworkshop.cn/?p=1274

  7. koa路由接口

    const router = require('koa-router')() //返回一个页面 router.get('/', async (ctx, next) => { global.con ...

  8. 关于使用 koa路由与mysql模块, ctx.body获取不到值的问题

    var Koa = require('koa');var Router = require('koa-router' );var bodyParser = require('koa-bodyparse ...

  9. BGP路由控制属性

    控制BGP路由概述: BGP与IGP不同,其着跟点主要在于不同的AS之间控制路由的传播和选择最佳路由 通过修改BGP基本属性可以实现基本的BGP路由控制和最佳路由的选择 引入其他路由协议发现的路由时. ...

随机推荐

  1. MockMVC

    随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的.从Spring 3.2开始Spring了Spring Web测试框架 Spring MVC测试框架提 ...

  2. Spring学习十 rest

    1:  Web  service:  是一个大的概念范畴,它表现了一种设计思想 SOAP 是 Web service 的一个重要组成部份. SOAP 是一种协议而非详细产品.SOAP 是通过 XML ...

  3. spring学习七

    一: web.xml中常用配置元素? <servlet></servlet>: 在向servlet或JSP页面制定初始化参数或定制URL时,首先命名servlet或JSP页面. ...

  4. Scala的Json序列化

    import java.util.TimeZone import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMappe ...

  5. 2015.7.24 CAD库中列举五字代码点所属航路及终端区图,左连接的累加

    select decode(fb.tupr,null,'仅航路',decode(fc.aw,null,'仅终端区','航路及终端区')) 范围,pt 五字代码点,fb.tupr 终端区图及程序,fc. ...

  6. java 多线程系列---JUC原子类(三)之AtomicLongArray原子类

    AtomicLongArray介绍和函数列表 在"Java多线程系列--“JUC原子类”02之 AtomicLong原子类"中介绍过,AtomicLong是作用是对长整形进行原子操 ...

  7. GCD详细介绍

    (1)是基于C语言的底层API (2)用Block定义任务,使用起来非常灵活便捷 (3)提供了更多的控制能力以及操作队列中所不能使用的底层函数 小结 说明:同步函数不具备开启线程的能力,无论是什么队列 ...

  8. LNMP 1.4 nginx启动脚本和配置文件

    编写Nginx启动脚本,写入下面这段,授权755 vim /etc/init.d/nginx #!/bin/bash # chkconfig: - # description: http servic ...

  9. ThinkPad E431按F1后直接进入系统无法进入BIOS

    联想的ThinkPad系列笔记本一般是按F1进如BIOS的,但是由于现在联想的笔记本多数都是预装Win 8或者更高版本的系统,所以有时候就没办法直接按F1进去BIOS.其原因是因为Win 8或者更高版 ...

  10. ajax跨域请求-jsonp

    1. 同源策略 ajax之所以需要“跨域”,罪魁祸首就是浏览器的同源策略.即,一个页面的ajax只能获取这个页面相同源或者相同域的数据. 如何叫“同源”或者“同域”呢?——协议.域名.端口号都必须相同 ...