meta-api 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +11 -0
 - data/Gemfile.lock +2 -2
 - data/README.md +32 -21
 - data/config/locales/zh-CN.yml +11 -3
 - data/docs//346/225/231/347/250/213.md +513 -171
 - data/examples/rails_app/Gemfile.lock +1 -1
 - data/lib/meta/application/execution.rb +27 -18
 - data/lib/meta/application/metadata.rb +1 -1
 - data/lib/meta/application/parameters.rb +17 -9
 - data/lib/meta/application/route.rb +6 -8
 - data/lib/meta/errors.rb +1 -1
 - data/lib/meta/json_schema/builders/schema_builder_tool.rb +1 -1
 - data/lib/meta/json_schema/schemas/base_schema.rb +2 -0
 - data/lib/meta/json_schema/support/type_converter.rb +13 -11
 - data/lib/meta/route_dsl/application_builder.rb +18 -10
 - data/lib/meta/route_dsl/around_action_builder.rb +30 -3
 - data/lib/meta/route_dsl/route_builder.rb +10 -4
 - data/meta-api.gemspec +1 -1
 - metadata +6 -6
 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # 教程
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            现有的 Web API  
     | 
| 
      
 3 
     | 
    
         
            +
            现有的 Web API 框架并不关注文档的问题,文档往往是作为插件挂载到框架上的。但是,文档和业务实现并不需要割裂开,它们在很大程度上应该是耦合在一起的。比方说,某个接口我定义了参数如此,就该自动生成一致的文档向前端告知;同样,当我提供了文档是如此后,我的接口实现就该自动地约束为这样实现。
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            Meta 框架天生就是将文档和实现统一起来的,并始终致力于此(如果真的有什么不一致或者不到位的地方,那只能说框架实现上尚有欠缺,并不能从思想上说本该如此)。Meta 框架与 Swagger 合作,致力于产生符合 Restful 和社区规范的文档格式。它提供了几乎完整的描述接口信息的宏命令,并且在描述接口的同时就能基本实现接口的一些约束,其中最重要的莫过于对参数和返回值的声明。
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
         @@ -8,341 +8,483 @@ Meta 框架天生就是将文档和实现统一起来的,并始终致力于此 
     | 
|
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            在正式阅读本教程之前,有一些*准备工作*需要提前了解的。
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            ###  
     | 
| 
      
 11 
     | 
    
         
            +
            ### 只接受 JSON
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 13 
     | 
    
         
            +
            **只接受格式为 `application/json` 的请求体参数,并且响应实体的格式一律为 `application/json`.**
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
               
         
     | 
| 
       17 
     | 
    
         
            -
               这在当前的环境下并不算太大的限制,如果你是致力于新项目的开发的话。但是,如果你处理旧项目,并且要求格式为 `application/json` 之外的格式,如 `application/xml`,则框架目前是无能为力的。
         
     | 
| 
       18 
     | 
    
         
            -
               
         
     | 
| 
       19 
     | 
    
         
            -
               这中限制只包括请求参数的实体,诸如路径里的参数、或 query 中的参数依然可用,不受该限制。
         
     | 
| 
      
 15 
     | 
    
         
            +
            这在当前的环境下并不算太大的限制,如果你是致力于新项目的开发的话。但是,如果你处理旧项目,并且要求格式为 `application/json` 之外的格式,如 `application/xml`,则框架目前是不能自动处理的。
         
     | 
| 
       20 
16 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
               
         
     | 
| 
       23 
     | 
    
         
            -
               表单上传有它特有的格式名,为 `multipart/form-data`. 因此,用框架提供的行为无法完成表单上传的格式。这并不是说无法完成表单上传的功能,你可能需要处理原生的 `Request` 对象(它是 Rack 架构提供的一个对象,参考 [Rack::Request](https://www.rubydoc.info/gems/rack/Rack/Request))。同样的,你如果要返回 `application/json` 之外的格式,你需要手动地处理原生的 `Response` 对象(同样也是 Rack 架构提供的一个对象,参考 [Rack::Response](https://www.rubydoc.info/gems/rack/Rack/Response))。
         
     | 
| 
      
 17 
     | 
    
         
            +
            这种限制只存在于通过 `params` 宏和 `status` 宏设定了请求体和响应体格式的情况。如果你没有用到这两个宏,那么你还是可以自由定义请求体和响应体的格式,只不过缺少了文档化的支持。自由实现需要用到 [`Rack::Request`](https://www.rubydoc.info/gems/rack/Rack/Request) 类和 [`Rack::Response`](https://www.rubydoc.info/gems/rack/Rack/Response) 类提供的方法,这是 Rack 架构提供的两个包装类,用于简化 HTTP 请求和响应操作。
         
     | 
| 
       24 
18 
     | 
    
         | 
| 
       25 
19 
     | 
    
         
             
            ### 教程脉络
         
     | 
| 
       26 
20 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
            首先,你将学到定义路由的全部知识。换句话说,你该如何具体地*描述*一个接口。一般来说,我们需要描述接口的标题、详述、标签、参数和返回值。
         
     | 
| 
       28 
22 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
            然后,你将学到命名空间的概念。命名空间用来组织接口的层级结构,并且会用到诸如如路由嵌套、before/after 钩子、异常拦截等概念。
         
     | 
| 
       30 
24 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
            从命名空间引申出的模块的概念也很重要。模块本身也是一个命名空间,命名空间用到的功能都可以用在模块中。除此之外,模块还用来组织大型应用的结构。最后,模块本身也是一个 Rack 应用,可以直接放在服务器下运行。
         
     | 
| 
       32 
26 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 27 
     | 
    
         
            +
            接下来是重点,我们将深入参数和返回值的定义。虽然说前面已经提到参数和返回值的知识,但仅覆盖最简单同时也是最常用的场景。参数和返回值的知识实在是太大了,有必要专门划出一个章节来介绍它。这里提一下,参数和返回值在 Meta 框架里都统一为一个叫做实体的概念,因此你只需要学会定义一种就能够同时定义两者了。
         
     | 
| 
       34 
28 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
            最后,将是一个生成文档的方法。虽然它很简单,仅仅是一个方法,但它如此重要以至于我不得不专门划出一个章节来强调它的重要性。
         
     | 
| 
       36 
30 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
      
 31 
     | 
    
         
            +
            文章的最后是特殊用法举例。说实话,我还没想好把它放哪,但它确实列举了几个比较常见的场景。
         
     | 
| 
       38 
32 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
            ### 继承和宏定义
         
     | 
| 
       40 
34 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
            在使用 Meta 框架提供的组件时,我们往往先要继承一个类,然后直接在类定义中使用宏命令。所谓的宏命令其实就是一个 Ruby 方法,只不过在 DSL 术语中我们将它称为“宏”。
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            例如,定义一个 API,我们继承的是 `Meta::Application` 类,然后在类中使用 `route` 宏定义路由:
         
     | 
| 
       42 
38 
     | 
    
         | 
| 
       43 
39 
     | 
    
         
             
            ```ruby
         
     | 
| 
       44 
40 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       45 
     | 
    
         
            -
              get do
         
     | 
| 
       46 
     | 
    
         
            -
                 
     | 
| 
       47 
     | 
    
         
            -
                action do
         
     | 
| 
       48 
     | 
    
         
            -
                  response.body = ["Hello, world!"]
         
     | 
| 
       49 
     | 
    
         
            -
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              route '/foo', :get do
         
     | 
| 
      
 42 
     | 
    
         
            +
                # ... 具体的宏定义
         
     | 
| 
       50 
43 
     | 
    
         
             
              end
         
     | 
| 
       51 
44 
     | 
    
         
             
            end
         
     | 
| 
       52 
45 
     | 
    
         
             
            ```
         
     | 
| 
       53 
46 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
            再比如继承 `Meta::Entity` 定义一个实体,实体内使用 `property` 宏定义属性:
         
     | 
| 
       55 
48 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
      
 49 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 50 
     | 
    
         
            +
            class UserEntity < Meta::Entity
         
     | 
| 
      
 51 
     | 
    
         
            +
              property :name
         
     | 
| 
      
 52 
     | 
    
         
            +
              property :age
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
      
 54 
     | 
    
         
            +
            ```
         
     | 
| 
       57 
55 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
            ## 路由定义(`route` 宏)
         
     | 
| 
       59 
57 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
      
 58 
     | 
    
         
            +
            在 `Meta::Application` 类内,第一个能做的事情就是定义路由。`route` 方法(以后我们称这种特定的 DSL 方法为“宏”)定义一个具体的路由(即接口):
         
     | 
| 
       61 
59 
     | 
    
         | 
| 
       62 
60 
     | 
    
         
             
            ```ruby
         
     | 
| 
       63 
61 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       64 
     | 
    
         
            -
               
     | 
| 
       65 
     | 
    
         
            -
                #  
     | 
| 
      
 62 
     | 
    
         
            +
              route '/', :get do
         
     | 
| 
      
 63 
     | 
    
         
            +
                # 块内定义路由的详细元素
         
     | 
| 
       66 
64 
     | 
    
         
             
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
      
 66 
     | 
    
         
            +
            ```
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            ###  HTTP 路径和方法
         
     | 
| 
       67 
69 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
      
 70 
     | 
    
         
            +
            `route` 方法接受一个路径字符串和一个 HTTP 方法,并且可接受一个块用于定义路由的详细元素(将在后面讲到)。HTTP 方法我们一共支持五种,包括 `get`、`post`、`put`、`patch`、`delete`. 为此,我们提供了五个便捷方法用于简化 `route` 方法调用的书写,举例:
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 73 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 74 
     | 
    
         
            +
              get do # 当路径为 `/` 时,路径参数可以省略
         
     | 
| 
       69 
75 
     | 
    
         
             
                # ...
         
     | 
| 
       70 
76 
     | 
    
         
             
              end
         
     | 
| 
       71 
77 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
               
     | 
| 
      
 78 
     | 
    
         
            +
              post '/foo' do
         
     | 
| 
       73 
79 
     | 
    
         
             
                # ...
         
     | 
| 
       74 
80 
     | 
    
         
             
              end
         
     | 
| 
       75 
81 
     | 
    
         | 
| 
      
 82 
     | 
    
         
            +
              put '/foo/bar' do
         
     | 
| 
      
 83 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
              
         
     | 
| 
      
 86 
     | 
    
         
            +
              patch '/foo/bar' do
         
     | 
| 
      
 87 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
              
         
     | 
| 
       76 
90 
     | 
    
         
             
              delete '/foo/bar' do
         
     | 
| 
       77 
91 
     | 
    
         
             
                # ...
         
     | 
| 
       78 
92 
     | 
    
         
             
              end
         
     | 
| 
       79 
93 
     | 
    
         
             
            end
         
     | 
| 
       80 
94 
     | 
    
         
             
            ```
         
     | 
| 
       81 
95 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
      
 96 
     | 
    
         
            +
            > 因为这种写法更为清晰并且视觉效果更好,教程的以后都用 `get`、`post`、`put`、`patch`、`delete` 五个方法代替 `route` 方法的调用。除非是只用到路径而不关心 HTTP 方法的情形。
         
     | 
| 
       83 
97 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
            ###  
     | 
| 
      
 98 
     | 
    
         
            +
            ### 通配符路径
         
     | 
| 
       85 
99 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
      
 100 
     | 
    
         
            +
            当定义路由 `route /foo/bar` 时,它匹配的是完整的路径 `/foo/bar`. 当你需要匹配一堆路径时,需要为路由加上通配符符号。`:` 和 `*` 是通配符符号的两种,前者匹配一个部分,后者尽可能多地匹配剩余的部份。这么说如果没说清楚,我举两个例子即可明白:
         
     | 
| 
       87 
101 
     | 
    
         | 
| 
       88 
102 
     | 
    
         
             
            - `/foo/:id`:它将匹配诸如 `/foo/1`、`/foo/bar` 等路径,但不能匹配 `/foo/a/b/c` 这样的路径。
         
     | 
| 
       89 
103 
     | 
    
         
             
            - `/foo/*path`:它可以匹配 `/foo`、`/foo/bar`、`/foo/a/b/c` 等格式的路径。
         
     | 
| 
       90 
104 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
      
 105 
     | 
    
         
            +
            > 通配符符号后面的单词(`id` 和 `path`)是参数名称,它将路由中与其匹配的部分放到参数中可访问。这里先提一下,通过 `request.params['id']`、`request.params['path']` 可以访问到路由当中匹配的部分。
         
     | 
| 
       92 
106 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
      
 107 
     | 
    
         
            +
            如果你不需要后续访问到参数,可以忽略命名:
         
     | 
| 
       94 
108 
     | 
    
         | 
| 
       95 
109 
     | 
    
         
             
            - `/foo/:`
         
     | 
| 
       96 
110 
     | 
    
         
             
            - `/foo/*`
         
     | 
| 
       97 
111 
     | 
    
         | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
      
 112 
     | 
    
         
            +
            再举两个路由参数的示例:
         
     | 
| 
       99 
113 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
            - `/foo/:id/bar`
         
     | 
| 
       101 
     | 
    
         
            -
            - `/foo/*/bar`
         
     | 
| 
      
 114 
     | 
    
         
            +
            - `/foo/:id/bar`:匹配诸如 `/foo/1/bar`、`/foo/2/bar` 等路径
         
     | 
| 
      
 115 
     | 
    
         
            +
            - `/foo/*/bar`:匹配 `/foo/bar`、`/foo/a/bar`、`/foo/a/b/bar`、`/foo/a/b/c/bar` 等格式的路径。
         
     | 
| 
       102 
116 
     | 
    
         | 
| 
       103 
     | 
    
         
            -
            ###  
     | 
| 
      
 117 
     | 
    
         
            +
            ### 定义路由的元信息(`meta` 宏)
         
     | 
| 
       104 
118 
     | 
    
         | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
      
 119 
     | 
    
         
            +
            在 `route` 宏内部,可使用两个宏: `meta` 宏定义路由的元信息,`action` 宏定义路由的执行逻辑。
         
     | 
| 
       106 
120 
     | 
    
         | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
            class DemoApp < Meta::Application
         
     | 
| 
       109 
     | 
    
         
            -
              namespace '/user' do
         
     | 
| 
       110 
     | 
    
         
            -
                get do
         
     | 
| 
       111 
     | 
    
         
            -
                  title '获取用户详情'
         
     | 
| 
       112 
     | 
    
         
            -
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
            首先,通过 `meta` 宏定义路由的“元”信息。注意,“元”信息的作用是双向的,既可以定义接口的文档,也可以约束接口的行为。例如,在 ` meta` 宏内定义参数:
         
     | 
| 
       113 
122 
     | 
    
         | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
      
 123 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 124 
     | 
    
         
            +
            post '/users' do
         
     | 
| 
      
 125 
     | 
    
         
            +
              meta do
         
     | 
| 
      
 126 
     | 
    
         
            +
                params do
         
     | 
| 
      
 127 
     | 
    
         
            +
                  param :name, type: 'string', description: '姓名'
         
     | 
| 
      
 128 
     | 
    
         
            +
                  param :age, type: 'integer', description: '年龄'
         
     | 
| 
       116 
129 
     | 
    
         
             
                end
         
     | 
| 
       117 
130 
     | 
    
         
             
              end
         
     | 
| 
       118 
131 
     | 
    
         
             
            end
         
     | 
| 
       119 
132 
     | 
    
         
             
            ```
         
     | 
| 
       120 
133 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 134 
     | 
    
         
            +
            它会产生两个方面的效果:
         
     | 
| 
       122 
135 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
      
 136 
     | 
    
         
            +
            1. 文档方面:接口文档的参数部分会暴露出两个参数:`name`、`age`,并声明它的类型和描述信息。
         
     | 
| 
      
 137 
     | 
    
         
            +
            2. 业务逻辑方面:业务代码执行时,通过标准的方法获取参数时会对参数作校验。这里面它只会提取出参数的两个字段(`name` 和 `age`),并对它们俩的类型作校验。如果参数不符合定义,会向客户端抛出一个错误。
         
     | 
| 
       124 
138 
     | 
    
         | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
      
 139 
     | 
    
         
            +
            #### `meta` 宏一览
         
     | 
| 
       126 
140 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
            class DemoApp < Meta::Application
         
     | 
| 
       129 
     | 
    
         
            -
              namespace '/user' do
         
     | 
| 
       130 
     | 
    
         
            -
                before do
         
     | 
| 
       131 
     | 
    
         
            -
                  @user = get_user
         
     | 
| 
       132 
     | 
    
         
            -
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
            `meta` 宏内部现在只提供了以下五个方法:
         
     | 
| 
       133 
142 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
      
 143 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 144 
     | 
    
         
            +
            post '/users' do
         
     | 
| 
      
 145 
     | 
    
         
            +
              meta do
         
     | 
| 
      
 146 
     | 
    
         
            +
                title '创建用户'
         
     | 
| 
      
 147 
     | 
    
         
            +
                description '接口的详细描述'
         
     | 
| 
      
 148 
     | 
    
         
            +
                tags ['User'] # 定义接口的 Tag,传递一个数组
         
     | 
| 
      
 149 
     | 
    
         
            +
                params do
         
     | 
| 
      
 150 
     | 
    
         
            +
                  # 内部定义参数结构
         
     | 
| 
       136 
151 
     | 
    
         
             
                end
         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                  title '更新用户详情'
         
     | 
| 
      
 152 
     | 
    
         
            +
                status 200 do
         
     | 
| 
      
 153 
     | 
    
         
            +
                  # 内部定义返回值结构
         
     | 
| 
       140 
154 
     | 
    
         
             
                end
         
     | 
| 
       141 
155 
     | 
    
         
             
              end
         
     | 
| 
       142 
156 
     | 
    
         
             
            end
         
     | 
| 
       143 
157 
     | 
    
         
             
            ```
         
     | 
| 
       144 
158 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
      
 159 
     | 
    
         
            +
            以上,`title`、`description`、`tags` 宏分别定义接口的标题、描述信息和标签列表。`params` 和 `status` 宏定义接口的参数和返回值,其内部定义比较复杂,将在后面详细讲解。
         
     | 
| 
       146 
160 
     | 
    
         | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
              before { puts 2 }
         
     | 
| 
       151 
     | 
    
         
            -
              around { |next_action|
         
     | 
| 
       152 
     | 
    
         
            -
                 puts 3
         
     | 
| 
       153 
     | 
    
         
            -
                 next_action.execute(self)
         
     | 
| 
       154 
     | 
    
         
            -
                 puts 5
         
     | 
| 
       155 
     | 
    
         
            -
              }
         
     | 
| 
       156 
     | 
    
         
            -
              after { puts 6 }
         
     | 
| 
       157 
     | 
    
         
            -
              after { puts 7 }
         
     | 
| 
      
 161 
     | 
    
         
            +
            #### `meta` 宏展开
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            `meta` 宏可以展开定义,亦即可以直接在 `route` 定义内部直接使用 `meta` 宏定义的语法,它是 `route` 定义内部提供的一种快捷方式:
         
     | 
| 
       158 
164 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
      
 165 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 166 
     | 
    
         
            +
            post '/users' do
         
     | 
| 
      
 167 
     | 
    
         
            +
              title '创建用户'
         
     | 
| 
      
 168 
     | 
    
         
            +
              description '接口的详细描述'
         
     | 
| 
      
 169 
     | 
    
         
            +
              tags ['User'] # 定义接口的 Tag,传递一个数组
         
     | 
| 
      
 170 
     | 
    
         
            +
              params do
         
     | 
| 
      
 171 
     | 
    
         
            +
                # 内部定义参数结构
         
     | 
| 
      
 172 
     | 
    
         
            +
              end
         
     | 
| 
      
 173 
     | 
    
         
            +
              status 200 do
         
     | 
| 
      
 174 
     | 
    
         
            +
                # 内部定义返回值结构
         
     | 
| 
       161 
175 
     | 
    
         
             
              end
         
     | 
| 
       162 
176 
     | 
    
         
             
            end
         
     | 
| 
       163 
177 
     | 
    
         
             
            ```
         
     | 
| 
       164 
178 
     | 
    
         | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
      
 179 
     | 
    
         
            +
            > 由于展开定义的方式写起来更加便捷,因此后面的教程示例都将采取这样的写法。
         
     | 
| 
       166 
180 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
       168 
     | 
    
         
            -
            2. 然后执行 `around` 钩子的前半部分,按照定义的顺序执行。
         
     | 
| 
       169 
     | 
    
         
            -
            3. 然后执行路由方法。
         
     | 
| 
       170 
     | 
    
         
            -
            4. 然后执行 `around` 钩子的后半部分,按照定义的顺序执行。
         
     | 
| 
       171 
     | 
    
         
            -
            5. 最后执行 `after` 钩子,按照定义的顺序执行。
         
     | 
| 
      
 181 
     | 
    
         
            +
            ### 定义路由的执行逻辑(`action` 宏)
         
     | 
| 
       172 
182 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
      
 183 
     | 
    
         
            +
            `action` 宏定义业务代码部分。将上面的 `POST /users` 接口的逻辑实现定义完全,大概率是以下这个样子:
         
     | 
| 
       174 
184 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
      
 185 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 186 
     | 
    
         
            +
            post '/users' do
         
     | 
| 
      
 187 
     | 
    
         
            +
              # ... 定义路由的 meta 部分
         
     | 
| 
      
 188 
     | 
    
         
            +
              action do
         
     | 
| 
      
 189 
     | 
    
         
            +
                user = User.create!(params[:user])
         
     | 
| 
      
 190 
     | 
    
         
            +
                render :user, user
         
     | 
| 
      
 191 
     | 
    
         
            +
              end
         
     | 
| 
      
 192 
     | 
    
         
            +
            end
         
     | 
| 
      
 193 
     | 
    
         
            +
            ```
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
            其中,用到的 `params` 方法和 `render` 方法将在后面讲到。
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            ## 层次化地定义路由(`namespace` 宏)
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            ### 使用 `namespace` 宏定义嵌套路由
         
     | 
| 
       176 
200 
     | 
    
         | 
| 
       177 
201 
     | 
    
         
             
            ```ruby
         
     | 
| 
       178 
202 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       179 
     | 
    
         
            -
               
     | 
| 
       180 
     | 
    
         
            -
                 
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
      
 203 
     | 
    
         
            +
              get do # 匹配 GET /
         
     | 
| 
      
 204 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 205 
     | 
    
         
            +
              end
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
              namespace '/foo' do
         
     | 
| 
      
 208 
     | 
    
         
            +
                get do # 匹配 GET /foo
         
     | 
| 
      
 209 
     | 
    
         
            +
                  # ...
         
     | 
| 
      
 210 
     | 
    
         
            +
                end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                post '/bar' do # 匹配 POST /foo/bar
         
     | 
| 
      
 213 
     | 
    
         
            +
                  # ...
         
     | 
| 
      
 214 
     | 
    
         
            +
                end
         
     | 
| 
      
 215 
     | 
    
         
            +
                
         
     | 
| 
      
 216 
     | 
    
         
            +
                namespace '/baz' do
         
     | 
| 
      
 217 
     | 
    
         
            +
                  # ... 匹配前缀为 /foo/baz
         
     | 
| 
       183 
218 
     | 
    
         
             
                end
         
     | 
| 
       184 
219 
     | 
    
         
             
              end
         
     | 
| 
       185 
220 
     | 
    
         
             
            end
         
     | 
| 
       186 
221 
     | 
    
         
             
            ```
         
     | 
| 
       187 
222 
     | 
    
         | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
      
 223 
     | 
    
         
            +
            `namespace` 宏是定义父级结构的,它不能定义到具体的路由,它的内部需要有 `namespace` 宏或 `route` 宏定义更具体的结点。**`namespace` 宏匹配的是路径的前缀。**
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
            而 `route` 宏是最深层次的结构,它定义的是具体的路由和它的行为,它的内部不能有 `namesapce` 宏及 `route` 宏。**`route` 宏要匹配完整的路径。**
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
            > 为什么要定义一个 `namespace` 宏,它不仅仅是减少路径的重复代码这么简单。综合来讲,`namespace` 宏有如下作用:
         
     | 
| 
      
 228 
     | 
    
         
            +
            >
         
     | 
| 
      
 229 
     | 
    
         
            +
            > 1. 通过组合 `namespace` 和 `route` 宏来定义应用的层次结构。
         
     | 
| 
      
 230 
     | 
    
         
            +
            > 2. `namespace` 内可定义钩子,用于公共的运行逻辑。
         
     | 
| 
      
 231 
     | 
    
         
            +
            > 3. 可定义 `namespace` 级的异常拦截处理方法。
         
     | 
| 
      
 232 
     | 
    
         
            +
            > 4. 在 `namespace` 定义 `meta` 宏,可定义公共的“元”信息。
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
            ### 钩子
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
            `namesapce` 内提供了两种钩子:`before`、`after`. 它在整个 `namespace` 层级执行一遍。
         
     | 
| 
       189 
237 
     | 
    
         | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
      
 238 
     | 
    
         
            +
            正如名字所表达的那样,`before` 在 `action` 宏之前执行,`after` 在 `action` 宏之后执行。
         
     | 
| 
       191 
239 
     | 
    
         | 
| 
       192 
240 
     | 
    
         
             
            ```ruby
         
     | 
| 
       193 
241 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       194 
242 
     | 
    
         
             
              namespace '/foo' do
         
     | 
| 
       195 
243 
     | 
    
         
             
                before do
         
     | 
| 
       196 
     | 
    
         
            -
                   
     | 
| 
      
 244 
     | 
    
         
            +
                  puts 1
         
     | 
| 
       197 
245 
     | 
    
         
             
                end
         
     | 
| 
       198 
246 
     | 
    
         | 
| 
      
 247 
     | 
    
         
            +
                after do
         
     | 
| 
      
 248 
     | 
    
         
            +
                  puts 2
         
     | 
| 
      
 249 
     | 
    
         
            +
                end
         
     | 
| 
      
 250 
     | 
    
         
            +
                
         
     | 
| 
       199 
251 
     | 
    
         
             
                get do
         
     | 
| 
       200 
252 
     | 
    
         
             
                  action do
         
     | 
| 
       201 
     | 
    
         
            -
                     
     | 
| 
       202 
     | 
    
         
            -
                    p @bar # nil
         
     | 
| 
      
 253 
     | 
    
         
            +
                    puts 3
         
     | 
| 
       203 
254 
     | 
    
         
             
                  end
         
     | 
| 
       204 
255 
     | 
    
         
             
                end
         
     | 
| 
       205 
256 
     | 
    
         | 
| 
       206 
     | 
    
         
            -
                 
     | 
| 
       207 
     | 
    
         
            -
                   
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                  end
         
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
       211 
     | 
    
         
            -
                  get do
         
     | 
| 
       212 
     | 
    
         
            -
                    action do
         
     | 
| 
       213 
     | 
    
         
            -
                      p @foo # 'foo'
         
     | 
| 
       214 
     | 
    
         
            -
                      p @bar # 'bar'
         
     | 
| 
       215 
     | 
    
         
            -
                    end
         
     | 
| 
      
 257 
     | 
    
         
            +
                put do
         
     | 
| 
      
 258 
     | 
    
         
            +
                  action do
         
     | 
| 
      
 259 
     | 
    
         
            +
                    puts 4
         
     | 
| 
       216 
260 
     | 
    
         
             
                  end
         
     | 
| 
       217 
261 
     | 
    
         
             
                end
         
     | 
| 
       218 
262 
     | 
    
         
             
              end
         
     | 
| 
       219 
263 
     | 
    
         
             
            end
         
     | 
| 
       220 
264 
     | 
    
         
             
            ```
         
     | 
| 
       221 
265 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
             
     | 
| 
      
 266 
     | 
    
         
            +
            当用户访问 `GET /foo` 接口时,依次打印数字 `1`、`3`、`2`;当用户访问 `PUT /foo` 接口时,依次打印数字 `1`、`4`、`2`.
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
            #### `around` 钩子(实验特性)
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
            Meta 框架同时还支持 `around` 钩子, `around` 钩子会包裹 `action` 执行:
         
     | 
| 
       223 
271 
     | 
    
         | 
| 
       224 
272 
     | 
    
         
             
            ```ruby
         
     | 
| 
       225 
273 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       226 
274 
     | 
    
         
             
              namespace '/foo' do
         
     | 
| 
       227 
     | 
    
         
            -
                 
     | 
| 
       228 
     | 
    
         
            -
                   
     | 
| 
      
 275 
     | 
    
         
            +
                around do |next_action|
         
     | 
| 
      
 276 
     | 
    
         
            +
                  puts 1
         
     | 
| 
      
 277 
     | 
    
         
            +
                  next_action.execute(self)
         
     | 
| 
      
 278 
     | 
    
         
            +
                  puts 2
         
     | 
| 
      
 279 
     | 
    
         
            +
                end
         
     | 
| 
      
 280 
     | 
    
         
            +
                
         
     | 
| 
      
 281 
     | 
    
         
            +
                get do
         
     | 
| 
      
 282 
     | 
    
         
            +
                  action do
         
     | 
| 
      
 283 
     | 
    
         
            +
                    puts 3
         
     | 
| 
      
 284 
     | 
    
         
            +
                  end
         
     | 
| 
       229 
285 
     | 
    
         
             
                end
         
     | 
| 
       230 
286 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
                 
     | 
| 
       232 
     | 
    
         
            -
                   
     | 
| 
       233 
     | 
    
         
            -
                     
     | 
| 
      
 287 
     | 
    
         
            +
                put do
         
     | 
| 
      
 288 
     | 
    
         
            +
                  action do
         
     | 
| 
      
 289 
     | 
    
         
            +
                    puts 4
         
     | 
| 
       234 
290 
     | 
    
         
             
                  end
         
     | 
| 
       235 
291 
     | 
    
         
             
                end
         
     | 
| 
       236 
292 
     | 
    
         
             
              end
         
     | 
| 
       237 
293 
     | 
    
         
             
            end
         
     | 
| 
       238 
294 
     | 
    
         
             
            ```
         
     | 
| 
       239 
295 
     | 
    
         | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
      
 296 
     | 
    
         
            +
            同样的,当用户访问 `GET /foo` 接口时,依次打印数字 `1`、`3`、`2`;当用户访问 `PUT /foo` 接口时,依次打印数字 `1`、`4`、`2`.
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
            > `around` 钩子现在还处于实验阶段,不建议实际开发中使用。当 `around` 钩子混合定义 `before`、`after` 钩子时,其执行的顺序比较混乱。而且,现在的 `around` 钩子还无法覆盖参数解析和返回值渲染的过程,这让它们的应用范围受到限制。当需要完整覆盖接口执行的全周期时,推荐使用 Rack 的中间件。最后,一定要在恰当的时机执行 `next_action.execute(self)`,否则后续的动作将不会得到执行。 `next_action.execute(self)` 调用稍显繁琐,不够优雅。
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
            #### 所有钩子的执行顺序
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
            如果只包含 `before` 和 `after` 钩子,则执行的顺序是:
         
     | 
| 
       241 
303 
     | 
    
         | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
      
 304 
     | 
    
         
            +
            1. `before` 钩子先执行,按照定义的顺序;
         
     | 
| 
      
 305 
     | 
    
         
            +
            2. 接着执行 `action` 定义的块;
         
     | 
| 
      
 306 
     | 
    
         
            +
            3. 最后执行 `after` 钩子,按照定义的顺序。
         
     | 
| 
      
 307 
     | 
    
         
            +
             
     | 
| 
      
 308 
     | 
    
         
            +
            举例(以下按照 `1`、`2`、`3` 的数字顺序执行):
         
     | 
| 
       243 
309 
     | 
    
         | 
| 
       244 
310 
     | 
    
         
             
            ```ruby
         
     | 
| 
       245 
     | 
    
         
            -
            class  
     | 
| 
       246 
     | 
    
         
            -
               
     | 
| 
       247 
     | 
    
         
            -
                 
     | 
| 
       248 
     | 
    
         
            -
             
     | 
| 
      
 311 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 312 
     | 
    
         
            +
              namespace '/foo' do
         
     | 
| 
      
 313 
     | 
    
         
            +
                before { puts 1 }
         
     | 
| 
      
 314 
     | 
    
         
            +
                before { puts 2 }
         
     | 
| 
      
 315 
     | 
    
         
            +
                after { puts 4 }
         
     | 
| 
      
 316 
     | 
    
         
            +
                after { puts 5 }
         
     | 
| 
       249 
317 
     | 
    
         | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
                  # 它将捕获 '/foo/bar' 路由下的异常
         
     | 
| 
      
 318 
     | 
    
         
            +
                get '/request' do
         
     | 
| 
      
 319 
     | 
    
         
            +
                  puts 3
         
     | 
| 
       253 
320 
     | 
    
         
             
                end
         
     | 
| 
       254 
321 
     | 
    
         
             
              end
         
     | 
| 
       255 
322 
     | 
    
         
             
            end
         
     | 
| 
       256 
323 
     | 
    
         
             
            ```
         
     | 
| 
       257 
324 
     | 
    
         | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
      
 325 
     | 
    
         
            +
            如果还包含 `around` 钩子,则会复杂一些,但大体上是:
         
     | 
| 
      
 326 
     | 
    
         
            +
             
     | 
| 
      
 327 
     | 
    
         
            +
            1. 最先执行的是 `before` 钩子以及 `around` 钩子的前半部分,按照定义的顺序;
         
     | 
| 
      
 328 
     | 
    
         
            +
            2. 接着执行 `action` 定义的块;
         
     | 
| 
      
 329 
     | 
    
         
            +
            3. 然后执行 `after` 钩子,按照定义的顺序;
         
     | 
| 
      
 330 
     | 
    
         
            +
            4. 最后执行 `around` 钩子的后半部分,按照定义的**逆序**执行。
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
            举例(以下按照 `1`、`2`、`3` 的数字顺序执行):
         
     | 
| 
       259 
333 
     | 
    
         | 
| 
       260 
334 
     | 
    
         
             
            ```ruby
         
     | 
| 
       261 
335 
     | 
    
         
             
            class DemoApp < Meta::Application
         
     | 
| 
       262 
336 
     | 
    
         
             
              namespace '/foo' do
         
     | 
| 
       263 
     | 
    
         
            -
                 
     | 
| 
      
 337 
     | 
    
         
            +
                before { puts 1 }
         
     | 
| 
      
 338 
     | 
    
         
            +
                around { |next_action|
         
     | 
| 
      
 339 
     | 
    
         
            +
                   puts 2
         
     | 
| 
      
 340 
     | 
    
         
            +
                   next_action.execute(self)
         
     | 
| 
      
 341 
     | 
    
         
            +
                   puts 9
         
     | 
| 
      
 342 
     | 
    
         
            +
                }
         
     | 
| 
      
 343 
     | 
    
         
            +
                around { |next_action|
         
     | 
| 
      
 344 
     | 
    
         
            +
                   puts 3
         
     | 
| 
      
 345 
     | 
    
         
            +
                   next_action.execute(self)
         
     | 
| 
      
 346 
     | 
    
         
            +
                   puts 8
         
     | 
| 
      
 347 
     | 
    
         
            +
                }
         
     | 
| 
      
 348 
     | 
    
         
            +
                before { puts 4 }
         
     | 
| 
      
 349 
     | 
    
         
            +
                after { puts 6 }
         
     | 
| 
      
 350 
     | 
    
         
            +
                after { puts 7 }
         
     | 
| 
      
 351 
     | 
    
         
            +
             
     | 
| 
      
 352 
     | 
    
         
            +
                get '/request' do
         
     | 
| 
      
 353 
     | 
    
         
            +
                  puts 5
         
     | 
| 
      
 354 
     | 
    
         
            +
                end
         
     | 
| 
       264 
355 
     | 
    
         
             
              end
         
     | 
| 
       265 
356 
     | 
    
         
             
            end
         
     | 
| 
       266 
357 
     | 
    
         
             
            ```
         
     | 
| 
       267 
358 
     | 
    
         | 
| 
       268 
     | 
    
         
            -
             
     | 
| 
      
 359 
     | 
    
         
            +
            #### 使用钩子的注意事项
         
     | 
| 
      
 360 
     | 
    
         
            +
             
     | 
| 
      
 361 
     | 
    
         
            +
            请注意,钩子的执行顺序是严格按照以上顺序执行的,与你定义的顺序无关。请确保 `before` 和 `around` 钩子优先于 `after` 的顺序定义,因为它们的执行也是优先于 `after` 的。
         
     | 
| 
      
 362 
     | 
    
         
            +
             
     | 
| 
      
 363 
     | 
    
         
            +
            另外,钩子的执行不会覆盖参数解析和返回值渲染,亦即 `before` 钩子在参数解析之后执行,`after` 钩子在返回值渲染之前执行,而 `around` 钩子亦不会覆盖参数解析和返回值渲染。
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
            钩子不会中断执行。如果要在钩子中中断程序的执行,可使用 `abort_execution!` 方法:
         
     | 
| 
       269 
366 
     | 
    
         | 
| 
       270 
367 
     | 
    
         
             
            ```ruby
         
     | 
| 
       271 
     | 
    
         
            -
             
     | 
| 
       272 
     | 
    
         
            -
               
     | 
| 
       273 
     | 
    
         
            -
               
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
               
     | 
| 
       276 
     | 
    
         
            -
               
     | 
| 
       277 
     | 
    
         
            -
               
     | 
| 
      
 368 
     | 
    
         
            +
            before do
         
     | 
| 
      
 369 
     | 
    
         
            +
              token = request.get_header('HTTP_X_TOKEN')
         
     | 
| 
      
 370 
     | 
    
         
            +
              // ... parse token
         
     | 
| 
      
 371 
     | 
    
         
            +
            rescue TokenInvalidError => e
         
     | 
| 
      
 372 
     | 
    
         
            +
              response.status = 401
         
     | 
| 
      
 373 
     | 
    
         
            +
              response.message = "Token 格式异常:#{e.message}"
         
     | 
| 
      
 374 
     | 
    
         
            +
              abort_execution!
         
     | 
| 
       278 
375 
     | 
    
         
             
            end
         
     | 
| 
       279 
376 
     | 
    
         
             
            ```
         
     | 
| 
       280 
377 
     | 
    
         | 
| 
       281 
     | 
    
         
            -
             
     | 
| 
      
 378 
     | 
    
         
            +
            `abort_execution!` 同时会跳过返回值渲染的执行。
         
     | 
| 
       282 
379 
     | 
    
         | 
| 
       283 
     | 
    
         
            -
             
     | 
| 
      
 380 
     | 
    
         
            +
            冷知识:`Meta::Application` 本身也可视为一个命名空间定义,`namespace` 内能用到的方法也可以在 `Meta::Application` 内使用。
         
     | 
| 
       284 
381 
     | 
    
         | 
| 
       285 
     | 
    
         
            -
             
     | 
| 
      
 382 
     | 
    
         
            +
            ### 异常拦截
         
     | 
| 
      
 383 
     | 
    
         
            +
             
     | 
| 
      
 384 
     | 
    
         
            +
            在 `namespace` 中可使用 `rescue_error` 拦截异常。
         
     | 
| 
       286 
385 
     | 
    
         | 
| 
       287 
386 
     | 
    
         
             
            ```ruby
         
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
               
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
             
     | 
| 
       292 
     | 
    
         
            -
             
     | 
| 
       293 
     | 
    
         
            -
                # 定义参数
         
     | 
| 
       294 
     | 
    
         
            -
                param :user do
         
     | 
| 
       295 
     | 
    
         
            -
                  param :name
         
     | 
| 
       296 
     | 
    
         
            -
                  param :age
         
     | 
| 
      
 387 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 388 
     | 
    
         
            +
              namespace '/users/:id' do
         
     | 
| 
      
 389 
     | 
    
         
            +
                rescue_error ActiveRecord::RecordNotFound do |e|
         
     | 
| 
      
 390 
     | 
    
         
            +
                  response.status = 404
         
     | 
| 
      
 391 
     | 
    
         
            +
                  response.body = ["所访问的资源不存在"]
         
     | 
| 
       297 
392 
     | 
    
         
             
                end
         
     | 
| 
       298 
     | 
    
         
            -
             
     | 
| 
       299 
     | 
    
         
            -
             
     | 
| 
       300 
     | 
    
         
            -
             
     | 
| 
       301 
     | 
    
         
            -
             
     | 
| 
       302 
     | 
    
         
            -
                   
     | 
| 
       303 
     | 
    
         
            -
                  expose :age
         
     | 
| 
      
 393 
     | 
    
         
            +
                
         
     | 
| 
      
 394 
     | 
    
         
            +
                get do
         
     | 
| 
      
 395 
     | 
    
         
            +
                  action do
         
     | 
| 
      
 396 
     | 
    
         
            +
                    user = User.find(params[:id])
         
     | 
| 
      
 397 
     | 
    
         
            +
                  end
         
     | 
| 
       304 
398 
     | 
    
         
             
                end
         
     | 
| 
       305 
399 
     | 
    
         
             
              end
         
     | 
| 
       306 
     | 
    
         
            -
              action do
         
     | 
| 
       307 
     | 
    
         
            -
                # 业务逻辑在这里实现,通过 params 方法访问参数,render 方法渲染实体
         
     | 
| 
       308 
     | 
    
         
            -
                user = get_user
         
     | 
| 
       309 
     | 
    
         
            -
                user.update!(params[:user])
         
     | 
| 
       310 
     | 
    
         
            -
                render :user, user
         
     | 
| 
       311 
     | 
    
         
            -
              end
         
     | 
| 
       312 
400 
     | 
    
         
             
            end
         
     | 
| 
       313 
401 
     | 
    
         
             
            ```
         
     | 
| 
       314 
402 
     | 
    
         | 
| 
       315 
     | 
    
         
            -
             
     | 
| 
      
 403 
     | 
    
         
            +
            以下是 Meta 框架抛出的异常:
         
     | 
| 
      
 404 
     | 
    
         
            +
             
     | 
| 
      
 405 
     | 
    
         
            +
            - `Meta::Errors::NoMatchingRoute`:路由不匹配时。
         
     | 
| 
      
 406 
     | 
    
         
            +
            - `Meta::Errors::ParameterInvalid`:参数存在异常时。
         
     | 
| 
      
 407 
     | 
    
         
            +
            - `Meta::Errors::RenderingInvalid`:响应值存在异常时。
         
     | 
| 
      
 408 
     | 
    
         
            +
            - `Meta::Errors::UnsupportedContentType`:框架只支持 `application/json` 的参数格式。当客户端的请求体不是这个格式时,会抛出这个错误。
         
     | 
| 
       316 
409 
     | 
    
         | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
      
 410 
     | 
    
         
            +
            #### 嵌套命名空间下的异常拦截
         
     | 
| 
      
 411 
     | 
    
         
            +
             
     | 
| 
      
 412 
     | 
    
         
            +
            拦截异常先在子作用域下拦截;如果拦截失败则继续在父作用域下拦截。下面的例子中:
         
     | 
| 
       318 
413 
     | 
    
         | 
| 
       319 
414 
     | 
    
         
             
            ```ruby
         
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
       321 
     | 
    
         
            -
               
     | 
| 
       322 
     | 
    
         
            -
                 
     | 
| 
       323 
     | 
    
         
            -
             
     | 
| 
       324 
     | 
    
         
            -
                tags ['User'] # 传递一个数组
         
     | 
| 
       325 
     | 
    
         
            -
                params do
         
     | 
| 
       326 
     | 
    
         
            -
                  # 定义参数
         
     | 
| 
      
 415 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 416 
     | 
    
         
            +
              namespace '/foo' do
         
     | 
| 
      
 417 
     | 
    
         
            +
                rescue_error ErrorOne do
         
     | 
| 
      
 418 
     | 
    
         
            +
                  puts "rescued in /foo" #(1)
         
     | 
| 
       327 
419 
     | 
    
         
             
                end
         
     | 
| 
       328 
     | 
    
         
            -
             
     | 
| 
       329 
     | 
    
         
            -
             
     | 
| 
      
 420 
     | 
    
         
            +
             
     | 
| 
      
 421 
     | 
    
         
            +
                rescue_error ErrorTwo do
         
     | 
| 
      
 422 
     | 
    
         
            +
                  puts "rescued in /foo" #(2)
         
     | 
| 
      
 423 
     | 
    
         
            +
                end
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
                namespace '/bar' do
         
     | 
| 
      
 426 
     | 
    
         
            +
                  rescue_error ErrorOne do
         
     | 
| 
      
 427 
     | 
    
         
            +
                    puts "rescued in /foo/bar" #(3)
         
     | 
| 
      
 428 
     | 
    
         
            +
                  end
         
     | 
| 
      
 429 
     | 
    
         
            +
                  
         
     | 
| 
      
 430 
     | 
    
         
            +
                  get do
         
     | 
| 
      
 431 
     | 
    
         
            +
                    action do
         
     | 
| 
      
 432 
     | 
    
         
            +
                    	raise ErrorOne
         
     | 
| 
      
 433 
     | 
    
         
            +
                  	end
         
     | 
| 
      
 434 
     | 
    
         
            +
                  end
         
     | 
| 
      
 435 
     | 
    
         
            +
                  
         
     | 
| 
      
 436 
     | 
    
         
            +
                  put do
         
     | 
| 
      
 437 
     | 
    
         
            +
                    action do
         
     | 
| 
      
 438 
     | 
    
         
            +
                    	raise ErrorTwo
         
     | 
| 
      
 439 
     | 
    
         
            +
                  	end
         
     | 
| 
      
 440 
     | 
    
         
            +
                  end
         
     | 
| 
       330 
441 
     | 
    
         
             
                end
         
     | 
| 
       331 
442 
     | 
    
         
             
              end
         
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
             
     | 
| 
      
 443 
     | 
    
         
            +
            end
         
     | 
| 
      
 444 
     | 
    
         
            +
            ```
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
            调用 `GET /foo/bar` 请求时会在(3)处被捕获;调用 `PUT /foo/bar` 请求时会在(2)处被捕获。
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
            #### `Meta::Errors::NoMatchingRoute` 只能在顶层被捕获
         
     | 
| 
      
 449 
     | 
    
         
            +
             
     | 
| 
      
 450 
     | 
    
         
            +
            由于框架实现的特殊性,异常 `Meta::Errors::NoMatchingRoute` 只会在顶层抛出。因此,只有在 `namespace` 的顶层捕获才有效果。
         
     | 
| 
      
 451 
     | 
    
         
            +
             
     | 
| 
      
 452 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 453 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 454 
     | 
    
         
            +
              # 在此捕获有效
         
     | 
| 
      
 455 
     | 
    
         
            +
              rescue_error Meta::Errors::NoMatchingRoute do |e|
         
     | 
| 
      
 456 
     | 
    
         
            +
                response.status = 404
         
     | 
| 
      
 457 
     | 
    
         
            +
                response.body = ["404 Not Found"]
         
     | 
| 
      
 458 
     | 
    
         
            +
              end
         
     | 
| 
      
 459 
     | 
    
         
            +
             
     | 
| 
      
 460 
     | 
    
         
            +
              namespace '/foo' do
         
     | 
| 
      
 461 
     | 
    
         
            +
                # 在此捕获无效
         
     | 
| 
      
 462 
     | 
    
         
            +
                rescue_error Meta::Errors::NoMatchingRoute do |e|
         
     | 
| 
      
 463 
     | 
    
         
            +
                  response.status = 404
         
     | 
| 
      
 464 
     | 
    
         
            +
                  response.body = ["404 Not Found"]
         
     | 
| 
      
 465 
     | 
    
         
            +
                end
         
     | 
| 
       334 
466 
     | 
    
         
             
              end
         
     | 
| 
       335 
467 
     | 
    
         
             
            end
         
     | 
| 
       336 
468 
     | 
    
         
             
            ```
         
     | 
| 
       337 
469 
     | 
    
         | 
| 
       338 
     | 
    
         
            -
             
     | 
| 
      
 470 
     | 
    
         
            +
            即使是上面的例子,调用 `GET /foo/bar` 请求时也只有顶层的异常拦截起了作用。
         
     | 
| 
       339 
471 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
            `namespace`  
     | 
| 
      
 472 
     | 
    
         
            +
            ### `namespace` 的 `meta` 宏
         
     | 
| 
      
 473 
     | 
    
         
            +
             
     | 
| 
      
 474 
     | 
    
         
            +
            同 `route` 宏内,`namespace` 宏内部可以定义 `meta` 宏。`namespace` 定义的 `meta` 宏定义下属路由的公共部分,其会应用到全部子路由,除非在 `route` 宏内复写。
         
     | 
| 
       341 
475 
     | 
    
         | 
| 
       342 
476 
     | 
    
         
             
            ```ruby
         
     | 
| 
       343 
     | 
    
         
            -
            namespace '/ 
     | 
| 
      
 477 
     | 
    
         
            +
            namespace '/users/:id' do
         
     | 
| 
      
 478 
     | 
    
         
            +
              # 以下 meta 内定义的部分会应用到 GET /users/:id 和 PUT /users/:id 两个路由。
         
     | 
| 
      
 479 
     | 
    
         
            +
              # 其中,因为 title 两个路由有重写,因此会使用两个路由自己的 title 定义。
         
     | 
| 
      
 480 
     | 
    
         
            +
              # description 两个路由都没有独自定义,因此会统一使用 meta 中的定义。
         
     | 
| 
      
 481 
     | 
    
         
            +
              # tags 同理,它们都挂载在同一个 Tag 下。
         
     | 
| 
      
 482 
     | 
    
         
            +
              # params 定义比较特殊,子路由下的定义不是复写而是补充。因此 GET /users/:id 包含一个参数 id,PUT /users/:id 包含了两个参数
         
     | 
| 
      
 483 
     | 
    
         
            +
              # id 和 user.
         
     | 
| 
      
 484 
     | 
    
         
            +
              # status 与 params 同理,但由于子路由内没有 status 定义,从而它们两个都是使用 meta 中的定义,即返回一个 user 属性。
         
     | 
| 
       344 
485 
     | 
    
         
             
              meta do
         
     | 
| 
       345 
     | 
    
         
            -
                 
     | 
| 
      
 486 
     | 
    
         
            +
                title '处理用户详情'
         
     | 
| 
      
 487 
     | 
    
         
            +
                description '通过路径参数获取用户数据,并对用户数据做一定的处理,比如查看、更新'
         
     | 
| 
       346 
488 
     | 
    
         
             
                tags ['User'] # 该 namespace 下的接口归到 User 标签下
         
     | 
| 
       347 
489 
     | 
    
         
             
                params do # 定义共同参数
         
     | 
| 
       348 
490 
     | 
    
         
             
                  param :id
         
     | 
| 
         @@ -375,6 +517,206 @@ namespace '/user/:id' do 
     | 
|
| 
       375 
517 
     | 
    
         
             
            end
         
     | 
| 
       376 
518 
     | 
    
         
             
            ```
         
     | 
| 
       377 
519 
     | 
    
         | 
| 
      
 520 
     | 
    
         
            +
            ## 路由的执行环境 (`Meta::Execution`)
         
     | 
| 
      
 521 
     | 
    
         
            +
             
     | 
| 
      
 522 
     | 
    
         
            +
            当路由在执行过程中,会将块绑定到一个 `Meta::Execution` 实例中执行。`before`、`after` 等钩子,`action` 定义的块内,以及异常拦截的过程中,其执行环境都会绑定到当前的  `Meta::Execution` 实例。
         
     | 
| 
      
 523 
     | 
    
         
            +
             
     | 
| 
      
 524 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 525 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 526 
     | 
    
         
            +
              before do
         
     | 
| 
      
 527 
     | 
    
         
            +
                @current_user = "Jim" # 设置一个环境变量
         
     | 
| 
      
 528 
     | 
    
         
            +
              end
         
     | 
| 
      
 529 
     | 
    
         
            +
              
         
     | 
| 
      
 530 
     | 
    
         
            +
              rescue_error StandardError do
         
     | 
| 
      
 531 
     | 
    
         
            +
                p @current_user # 可以访问先前设置的实例变量
         
     | 
| 
      
 532 
     | 
    
         
            +
              end
         
     | 
| 
      
 533 
     | 
    
         
            +
              
         
     | 
| 
      
 534 
     | 
    
         
            +
              get '/user' do
         
     | 
| 
      
 535 
     | 
    
         
            +
                action do
         
     | 
| 
      
 536 
     | 
    
         
            +
                  p @current_user # 可以访问先前设置的实例变量
         
     | 
| 
      
 537 
     | 
    
         
            +
                  raise # 抛出异常
         
     | 
| 
      
 538 
     | 
    
         
            +
                end
         
     | 
| 
      
 539 
     | 
    
         
            +
              end
         
     | 
| 
      
 540 
     | 
    
         
            +
            end
         
     | 
| 
      
 541 
     | 
    
         
            +
            ```
         
     | 
| 
      
 542 
     | 
    
         
            +
             
     | 
| 
      
 543 
     | 
    
         
            +
            ### `Meta::Execution` 提供的方法。
         
     | 
| 
      
 544 
     | 
    
         
            +
             
     | 
| 
      
 545 
     | 
    
         
            +
            #### `#request`
         
     | 
| 
      
 546 
     | 
    
         
            +
             
     | 
| 
      
 547 
     | 
    
         
            +
            `request` 方法返回 [`Rack::Request`](https://www.rubydoc.info/gems/rack/Rack/Request) 实例,它是 Rack 框架提供的包装类,用于简化 HTTP 操作。
         
     | 
| 
      
 548 
     | 
    
         
            +
             
     | 
| 
      
 549 
     | 
    
         
            +
            #### `#response`
         
     | 
| 
      
 550 
     | 
    
         
            +
             
     | 
| 
      
 551 
     | 
    
         
            +
            `response` 方法返回 [`Rack::Response`](https://www.rubydoc.info/gems/rack/Rack/Response) 的实例,它是 Rack 框架的包装类,用于简化 HTTP 操作。
         
     | 
| 
      
 552 
     | 
    
         
            +
             
     | 
| 
      
 553 
     | 
    
         
            +
            #### `#params`
         
     | 
| 
      
 554 
     | 
    
         
            +
             
     | 
| 
      
 555 
     | 
    
         
            +
            不要与 `.params` 宏所混淆,它是 `Meta::Execution` 提供的实例方法,返回解析后的参数。参数的解析参考 `params` 宏的定义。
         
     | 
| 
      
 556 
     | 
    
         
            +
             
     | 
| 
      
 557 
     | 
    
         
            +
            #### `#render`
         
     | 
| 
      
 558 
     | 
    
         
            +
             
     | 
| 
      
 559 
     | 
    
         
            +
            定义实际响应体时使用 `render` 方法。`render` 方法参考 `status` 定义的响应体格式过滤和验证字段。
         
     | 
| 
      
 560 
     | 
    
         
            +
             
     | 
| 
      
 561 
     | 
    
         
            +
            #### `#abort_execution!`
         
     | 
| 
      
 562 
     | 
    
         
            +
             
     | 
| 
      
 563 
     | 
    
         
            +
            中断后续的执行。如果是在 `before` 块中执行这个方法,则跳过后续的 `before`、`action` 和 `after` 块;如果是在 `action` 块中执行这个方法,则跳过 `after` 块。注意,这个方法会跳过响应体渲染阶段。
         
     | 
| 
      
 564 
     | 
    
         
            +
             
     | 
| 
      
 565 
     | 
    
         
            +
            ### 共享模块
         
     | 
| 
      
 566 
     | 
    
         
            +
             
     | 
| 
      
 567 
     | 
    
         
            +
            不要在 `Meta::Application` 内部定义方法,在它内部直接定义的方法应用到 `Meta::Execution` 实例。如果需要在当前以及后续路由用到公共的方法,可以在 `shared` 块内定义:
         
     | 
| 
      
 568 
     | 
    
         
            +
             
     | 
| 
      
 569 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 570 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 571 
     | 
    
         
            +
              shared do
         
     | 
| 
      
 572 
     | 
    
         
            +
                def current_user
         
     | 
| 
      
 573 
     | 
    
         
            +
                  @current_user
         
     | 
| 
      
 574 
     | 
    
         
            +
                end
         
     | 
| 
      
 575 
     | 
    
         
            +
              end
         
     | 
| 
      
 576 
     | 
    
         
            +
              
         
     | 
| 
      
 577 
     | 
    
         
            +
              before do
         
     | 
| 
      
 578 
     | 
    
         
            +
                @current_user = "Jim" # 设置一个环境变量
         
     | 
| 
      
 579 
     | 
    
         
            +
              end
         
     | 
| 
      
 580 
     | 
    
         
            +
              
         
     | 
| 
      
 581 
     | 
    
         
            +
              get '/user' do
         
     | 
| 
      
 582 
     | 
    
         
            +
                action do
         
     | 
| 
      
 583 
     | 
    
         
            +
                  p current_user # 在路由内访问方法
         
     | 
| 
      
 584 
     | 
    
         
            +
                end
         
     | 
| 
      
 585 
     | 
    
         
            +
              end
         
     | 
| 
      
 586 
     | 
    
         
            +
              
         
     | 
| 
      
 587 
     | 
    
         
            +
              namespace '/foo' do
         
     | 
| 
      
 588 
     | 
    
         
            +
                get '/user' do
         
     | 
| 
      
 589 
     | 
    
         
            +
                  action do
         
     | 
| 
      
 590 
     | 
    
         
            +
                    p current_user # 在子命名空间内也能访问到方法
         
     | 
| 
      
 591 
     | 
    
         
            +
                  end
         
     | 
| 
      
 592 
     | 
    
         
            +
                end
         
     | 
| 
      
 593 
     | 
    
         
            +
              end
         
     | 
| 
      
 594 
     | 
    
         
            +
            end
         
     | 
| 
      
 595 
     | 
    
         
            +
            ```
         
     | 
| 
      
 596 
     | 
    
         
            +
             
     | 
| 
      
 597 
     | 
    
         
            +
            除此之外,`shared` 的参数也接受模块。
         
     | 
| 
      
 598 
     | 
    
         
            +
             
     | 
| 
      
 599 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 600 
     | 
    
         
            +
            module HelperFoo
         
     | 
| 
      
 601 
     | 
    
         
            +
              def foo; 'foo' end
         
     | 
| 
      
 602 
     | 
    
         
            +
            end
         
     | 
| 
      
 603 
     | 
    
         
            +
             
     | 
| 
      
 604 
     | 
    
         
            +
            module HelperBar
         
     | 
| 
      
 605 
     | 
    
         
            +
              def bar; 'bar' end
         
     | 
| 
      
 606 
     | 
    
         
            +
            end
         
     | 
| 
      
 607 
     | 
    
         
            +
             
     | 
| 
      
 608 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 609 
     | 
    
         
            +
              shared HelperFoo, HelperBar
         
     | 
| 
      
 610 
     | 
    
         
            +
              
         
     | 
| 
      
 611 
     | 
    
         
            +
              get '/user' do
         
     | 
| 
      
 612 
     | 
    
         
            +
                action do
         
     | 
| 
      
 613 
     | 
    
         
            +
                  p foo
         
     | 
| 
      
 614 
     | 
    
         
            +
                  p bar # 可访问模块定义的方法
         
     | 
| 
      
 615 
     | 
    
         
            +
                end
         
     | 
| 
      
 616 
     | 
    
         
            +
              end
         
     | 
| 
      
 617 
     | 
    
         
            +
            end
         
     | 
| 
      
 618 
     | 
    
         
            +
            ```
         
     | 
| 
      
 619 
     | 
    
         
            +
             
     | 
| 
      
 620 
     | 
    
         
            +
            ## 模块(`Meta::Application`)
         
     | 
| 
      
 621 
     | 
    
         
            +
             
     | 
| 
      
 622 
     | 
    
         
            +
            ### `Meta::Application` 等同于 `namespace` 定义
         
     | 
| 
      
 623 
     | 
    
         
            +
             
     | 
| 
      
 624 
     | 
    
         
            +
            要知道 `Meta::Application`,第一个事情就是它等同于 `namespace` 定义。像 `namespace` 一样,能定义路由、钩子、异常拦截的地方都可以在 `Meta::Application` 内直接定义。
         
     | 
| 
      
 625 
     | 
    
         
            +
             
     | 
| 
      
 626 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 627 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 628 
     | 
    
         
            +
              # meta 定义,能应用到下属子路由的所有地方
         
     | 
| 
      
 629 
     | 
    
         
            +
              meta do
         
     | 
| 
      
 630 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 631 
     | 
    
         
            +
              end
         
     | 
| 
      
 632 
     | 
    
         
            +
              
         
     | 
| 
      
 633 
     | 
    
         
            +
              # 它将捕获下属子路由的所有异常
         
     | 
| 
      
 634 
     | 
    
         
            +
              rescue_error Exception do
         
     | 
| 
      
 635 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 636 
     | 
    
         
            +
              end
         
     | 
| 
      
 637 
     | 
    
         
            +
              
         
     | 
| 
      
 638 
     | 
    
         
            +
              # 钩子,最先执行
         
     | 
| 
      
 639 
     | 
    
         
            +
              before do
         
     | 
| 
      
 640 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 641 
     | 
    
         
            +
              end
         
     | 
| 
      
 642 
     | 
    
         
            +
              
         
     | 
| 
      
 643 
     | 
    
         
            +
              # 钩子,最后执行
         
     | 
| 
      
 644 
     | 
    
         
            +
              after do
         
     | 
| 
      
 645 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 646 
     | 
    
         
            +
              end
         
     | 
| 
      
 647 
     | 
    
         
            +
             
     | 
| 
      
 648 
     | 
    
         
            +
              # 定义嵌套命名空间
         
     | 
| 
      
 649 
     | 
    
         
            +
              namespace '/...' do
         
     | 
| 
      
 650 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 651 
     | 
    
         
            +
              end
         
     | 
| 
      
 652 
     | 
    
         
            +
              
         
     | 
| 
      
 653 
     | 
    
         
            +
              # 也可以直接定义路由
         
     | 
| 
      
 654 
     | 
    
         
            +
              route '/...', :post do
         
     | 
| 
      
 655 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 656 
     | 
    
         
            +
              end
         
     | 
| 
      
 657 
     | 
    
         
            +
            end
         
     | 
| 
      
 658 
     | 
    
         
            +
            ```
         
     | 
| 
      
 659 
     | 
    
         
            +
             
     | 
| 
      
 660 
     | 
    
         
            +
            你可以将 `Meta::Application ` 视为路径定义为 `/` 的命名空间。
         
     | 
| 
      
 661 
     | 
    
         
            +
             
     | 
| 
      
 662 
     | 
    
         
            +
            ### `Meta::Application` 是可复用的模块
         
     | 
| 
      
 663 
     | 
    
         
            +
             
     | 
| 
      
 664 
     | 
    
         
            +
            遇到大型项目时,将 API 定义分离成若干个单独的文件更好的组织。做到这一点,就用到 `namespace` 中提供的 `apply` 方法。
         
     | 
| 
      
 665 
     | 
    
         
            +
             
     | 
| 
      
 666 
     | 
    
         
            +
            继承自 `Meta::Application` 的类都是一个模块,它可以在 `namespace` 中被复用。
         
     | 
| 
      
 667 
     | 
    
         
            +
             
     | 
| 
      
 668 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 669 
     | 
    
         
            +
            class Foo < Meta::Application
         
     | 
| 
      
 670 
     | 
    
         
            +
              route '/foo' do
         
     | 
| 
      
 671 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 672 
     | 
    
         
            +
              end
         
     | 
| 
      
 673 
     | 
    
         
            +
            end
         
     | 
| 
      
 674 
     | 
    
         
            +
             
     | 
| 
      
 675 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 676 
     | 
    
         
            +
              apply Foo
         
     | 
| 
      
 677 
     | 
    
         
            +
            end
         
     | 
| 
      
 678 
     | 
    
         
            +
            ```
         
     | 
| 
      
 679 
     | 
    
         
            +
             
     | 
| 
      
 680 
     | 
    
         
            +
            将定义写在一个类里,其等价于:
         
     | 
| 
      
 681 
     | 
    
         
            +
             
     | 
| 
      
 682 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 683 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 684 
     | 
    
         
            +
              route '/foo' do
         
     | 
| 
      
 685 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 686 
     | 
    
         
            +
              end
         
     | 
| 
      
 687 
     | 
    
         
            +
            end
         
     | 
| 
      
 688 
     | 
    
         
            +
            ```
         
     | 
| 
      
 689 
     | 
    
         
            +
             
     | 
| 
      
 690 
     | 
    
         
            +
            `apply` 方法还可跟一个参数 `tags: [...]`,统一覆盖被引入的模块在渲染文档时声明的 `tags`:
         
     | 
| 
      
 691 
     | 
    
         
            +
             
     | 
| 
      
 692 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 693 
     | 
    
         
            +
            class OpenAPIApp < Meta::Application
         
     | 
| 
      
 694 
     | 
    
         
            +
              apply API::Logins, tags: ['Login']
         
     | 
| 
      
 695 
     | 
    
         
            +
              apply API::Users, tags: ['User']
         
     | 
| 
      
 696 
     | 
    
         
            +
              apply API::Organizations, tags: ['Organization']
         
     | 
| 
      
 697 
     | 
    
         
            +
              apply API::Projects, tags: ['Project']
         
     | 
| 
      
 698 
     | 
    
         
            +
              apply API::Versions, tags: ['Version']
         
     | 
| 
      
 699 
     | 
    
         
            +
              apply API::Members, tags: ['Member']
         
     | 
| 
      
 700 
     | 
    
         
            +
            end
         
     | 
| 
      
 701 
     | 
    
         
            +
            ```
         
     | 
| 
      
 702 
     | 
    
         
            +
             
     | 
| 
      
 703 
     | 
    
         
            +
            ### `Meta::Application` 是一个 Rack 应用
         
     | 
| 
      
 704 
     | 
    
         
            +
             
     | 
| 
      
 705 
     | 
    
         
            +
            `Meta::Application` 同时也是一个 Rack 应用,将它挂载在 Rack 下可以直接作为一个服务运行。我们看一个最简单的 `Meta::Application` 实例:
         
     | 
| 
      
 706 
     | 
    
         
            +
             
     | 
| 
      
 707 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 708 
     | 
    
         
            +
            class DemoApp < Meta::Application
         
     | 
| 
      
 709 
     | 
    
         
            +
              route '/', :get do
         
     | 
| 
      
 710 
     | 
    
         
            +
                title '应用的根路径'
         
     | 
| 
      
 711 
     | 
    
         
            +
                action do
         
     | 
| 
      
 712 
     | 
    
         
            +
                  response.body = ["Hello, world!"]
         
     | 
| 
      
 713 
     | 
    
         
            +
                end
         
     | 
| 
      
 714 
     | 
    
         
            +
              end
         
     | 
| 
      
 715 
     | 
    
         
            +
            end
         
     | 
| 
      
 716 
     | 
    
         
            +
            ```
         
     | 
| 
      
 717 
     | 
    
         
            +
             
     | 
| 
      
 718 
     | 
    
         
            +
            > 将它挂载在 Rack 下并访问 `http://localhost:9292` 你将看到接口返回 `"Hello, world"` 文本。
         
     | 
| 
      
 719 
     | 
    
         
            +
             
     | 
| 
       378 
720 
     | 
    
         
             
            ## 参数定义
         
     | 
| 
       379 
721 
     | 
    
         | 
| 
       380 
722 
     | 
    
         
             
            本节介绍参数和返回值如何定义。因为 Meta 框架在底层不区分参数和返回值,它们都统一为“实体”的概念。因此,当涉及到语法细节时,在参数、返回值、实体内都是一致的。
         
     |