Routing是web服务中重要的组成部分,用于调度请求和返回.
Vapor的Routing提供了RouteBuilder和RouteCollection
其中RouteBuilder提供了基本的路由和路由集
我们先看看部分源码,看看到底能干些什么:
extension RouteBuilder { public func add(_ method: HTTP.Method, _ path: String..., value: @escaping Routing.RouteHandler) public func socket(_ segments: String..., handler: @escaping Routing.WebSocketRouteHandler) public func all(_ segments: String..., handler: @escaping Routing.RouteHandler) public func get(_ segments: String..., handler: @escaping Routing.RouteHandler) public func post(_ segments: String..., handler: @escaping Routing.RouteHandler) public func put(_ segments: String..., handler: @escaping Routing.RouteHandler) public func patch(_ segments: String..., handler: @escaping Routing.RouteHandler) public func delete(_ segments: String..., handler: @escaping Routing.RouteHandler) public func options(_ segments: String..., handler: @escaping Routing.RouteHandler) }
从源码可以看到基本的网络请求RouteBuilder都可以提供,包括HTTP请求 POST, GET, PUT, PATCH, DELETE,以及socket请求和all, add, patch, options
下面我来一一介绍他们的用法:
创建一个Routes+Test.swift的文件,并加入以下测试代码
import Vapor extension Droplet { func setupTestRoutes() throws { post("testPost") { req in return "testPost result" } get("testGet") { req in return "testGet result" } put("testPut") { req in return "testPut result" } patch("testPatch") { req in return "testPatch result" } delete("testDelete") { req in return "testDelete result" } options("testOptions") { req in return "testOptions result" } } }
接着在Droplet+Setup.swift里面加入try setupTestRoutes(),整体代码如下
@_exported import Vapor extension Droplet { public func setup() throws { try setupRoutes() try setupTestRoutes() } }
运行程序,就可以测试这些接口了,请注意,只有get请求才能直接在浏览器输入http://0.0.0.0:8080/textGet 输出 testGet result
对于其他接口可以通过接口测试工具来测试,这里我推荐使用cocoa rest client
可以很快测试接口并查看返回的结果
cocoa rest client界面
参数有两种写法: 一种是 :[类型.parameter] 另一种是: [:参数名称]
我们写一个测试的请求:
get("age", Int.parameter) { req in let age = try req.parameters.next(Int.self) return "Age is \(age)" } get("call", ":name") { req in guard let name = req.parameters["name"]?.string else { throw Abort.badRequest } return "You requested User #\(name)" }
那么 请求 http://0.0.0.0:8080/age/18 则会返回 Age is 18 请求http://0.0.0.0:8080/call/Leakey则会返回Calling Leakey
如果参数是一个对象,则需要对象适配Parameterizable协议
Parameterizable的源码如下:
public protocol Parameterizable { /// the unique key to use as a slug in route building static var uniqueSlug: String { get } // returns the found model for the resolved url parameter static func make(for parameter: String) throws -> Self }
我们可以写一个User对象:
extension Type: Parameterizable { static var uniqueSlug: String { return "type" } static func make(for parameter: String) throws -> Type { } }
那么请求中就可以将Type作为参数了
drop.get("users", "nickname", Type.parameter) { req in let foo = try req.parameters.next(Type.self) ... }
看看路由集的源码
extension RouteBuilder { /// Group all subsequent routes built with this builder /// under this specified host /// /// the last host in the chain will take precedence, for example: /// /// if using: /// grouped(host: "0.0.0.0").grouped(host: "196.08.0.1") /// /// will bind subsequent additions to '196.08.0.1' public func grouped(host: String) -> RouteBuilder /// Group all subsequent routes behind a specified path prefix /// use `,` separated list or `/` separated string /// for example, the following are all equal /// /// "a/path/to/foo" /// "a", "path", "to", "foo" /// "a/path", "to/foo" public func grouped(_ path: String...) -> RouteBuilder /// - see grouped(_ path: String...) public func grouped(_ path: [String]) -> RouteBuilder /// Group all subsequent routes to pass through specified middleware /// use `,` separated list for multiple middleware public func grouped(_ middleware: Middleware...) -> RouteBuilder /// - see grouped(middleware: Middleware...) public func grouped(_ middleware: [Middleware]) -> RouteBuilder } extension RouteBuilder { /// Closure based variant of grouped(host: String) public func group(host: String, handler: (RouteBuilder) -> ()) /// Closure based variant of grouped(_ path: String...) public func group(_ path: String..., handler: (RouteBuilder) -> ()) /// Closure based variant of grouped(_ path: [String]) public func group(path: [String], handler: (RouteBuilder) -> ()) /// Closure based variant of grouped(middleware: Middleware...) public func group(_ middleware: Middleware..., handler: (RouteBuilder) -> ()) /// Closure based variant of grouped(middleware: [Middleware]) public func group(middleware: [Middleware], handler: (RouteBuilder) -> ()) }
可以看到路由组包含一系列名为grouped和名为group的方法
那么,group有什么作用呢? 官方文档给出的解释是:
Grouping routes together makes it easy to add common prefixes, middleware, or hosts to multiple routes.
这里我详细解释一下,路由集的作用是将许多的路由集合在一起,比如统一前缀的不同请求集合在一起,中间件的集合,以及主机的集合。
同前面一样,我添加了一个测试方法
func setupGroupRoutes() throws { group("testGroup") { testGroup in testGroup.post("testGroup_post") { request in return "testGroup testGroup_post result" } testGroup.get("testGroup_get") { request in return "testGroup testGroup_get result" } testGroup.put("testGroup_put") { req in return "testGroup testGroup_put result" } testGroup.patch("testGroup_patch") { req in return "testGroup testGroup_patch result" } testGroup.delete("testGroup_delete") { req in return "testGroup testGroup_delete result" } testGroup.options("testGroup_options") { req in return "testGroup testGroup_options result" } } }
接着在Droplet+Setup.swift里面加入try setupGroupRoutes(),整体代码如下
@_exported import Vapor extension Droplet { public func setup() throws { try setupRoutes() try setupTestRoutes() try setupGroupRoutes() } }
对group的测试
这里的group和java的spring的package有些类似。
上面我们创建了名为testGroup的group,要获得这个group的RouteBuilder,就可以用grouped方法了,这样就可以继续给group增加子请求了。
如下面的代码:
let testGroup = grouped("testGroup") testGroup.get("extra") { request in return "testGroup extra result" }
中间件的请求集,官方给出的例子是auth,引入AuthProvider到项目即可使用auth中间件
drop.group(AuthMiddleware()) { authorized in authorized.get("token") { request in // has been authorized } }
这个例子告诉我们,我们可以使用中间件来处理请求,如使用auth中间件来校验token,验证数据等。
drop.group(host: "vapor.codes") { vapor in vapor.get { request in // only responds to requests to vapor.codes } }
drop.grouped(host: "vapor.codes").grouped(AuthMiddleware()).group("v1") { authedSecureV1 in // add routes here }
RouteCollection是一个协议, 代码很简单,只有一个方法需要实现:
public protocol RouteCollection { func build(_ builder: RouteBuilder) throws }
如果对上面的group已经理解的同学这里就不难理解了,这里提供了一个RouteBuilder,可以通过这个builder获得group,并增加新的请求到group中,下面是我的demo:
class TestCollection: RouteCollection { func build(_ builder: RouteBuilder) throws { let testGroup = builder.grouped("testGroup") testGroup.get("anotherExtra") { req in return "testGroup extra result" } } }
需要在Droplet+Setup.swift中添加这个collection的实例才能有效
@_exported import Vapor extension Droplet { public func setup() throws { try setupRoutes() try setupTestRoutes() try setupGroupRoutes() let testCollection = TestCollection() try collection(testCollection) } }
如果collection同时实现了RouteCollection 和 EmptyInitializable协议,并添加了空的init方法,就可以用XXXCollection.self添加到droplet中了,我们可以改造一下前面的demo:
class TestCollection: RouteCollection, EmptyInitializable { required init() { } func build(_ builder: RouteBuilder) throws { let testGroup = builder.grouped("testGroup") testGroup.get("anotherExtra") { req in return "testGroup extra result" } } }
Droplet+Setup.swift
@_exported import Vapor extension Droplet { public func setup() throws { try setupRoutes() try setupTestRoutes() try setupGroupRoutes() try collection(TestCollection.self) } }
代码是不是又简洁了许多?
路由的基本使用就介绍到这里,如果有遗漏或者不清楚的地方请提醒我补充,希望能对你有所帮助。
国际惯例,Demo请见HelloVapor
关于Vapor其他知识,可以参考以下文章:
Vapor奇幻之旅(01开始) Vapor奇幻之旅(02部署) Vapor奇幻之旅(03上手) Vapor奇幻之旅(04Routing) Vapor奇幻之旅(05 Fluent) Vapor奇幻之旅(06 PostgreSQL) Vapor奇幻之旅(07 连接服务端PostgreSQL) Vapor奇幻之旅(08 连接服务端MongoDB) Vapor奇幻之旅(09 连接MySQL)
希望你对我的教程能够喜欢,你们的赞是我持续的动力,欢迎加入QQ群参与互动:431296189
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句