Routing是web服務中重要的組成部分红竭,用于調(diào)度請求和返回.
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)
}
從源碼可以看到基本的網(wǎng)絡請求RouteBuilder都可以提供喘落,包括HTTP請求 POST, GET, PUT, PATCH, DELETE,以及socket請求和all, add, patch, options
下面我來一一介紹他們的用法:
創(chuàng)建一個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
可以很快測試接口并查看返回的結果
-
請求參數(shù)的添加
通過前面的源碼我們可以看到基本請求的方法第一個參數(shù)是 segments: String... 鹿蜀,也就是可以傳入多個參數(shù)箕慧, 通常我們的get請求是需要帶有參數(shù)的,如傳入名字茴恰,年齡颠焦。
參數(shù)有兩種寫法:
一種是 :[類型.parameter]
另一種是: [:參數(shù)名稱]
我們寫一個測試的請求:
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
如果參數(shù)是一個對象,則需要對象適配Parameterizable協(xié)議
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作為參數(shù)了
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.
這里我詳細解釋一下,路由集的作用是將許多的路由集合在一起分冈,比如統(tǒng)一前綴的不同請求集合在一起圾另,中間件的集合,以及主機的集合雕沉。
- Group
同前面一樣集乔,我添加了一個測試方法
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和java的spring的package有些類似坡椒。
- Grouped
上面我們創(chuàng)建了名為testGroup的group扰路,要獲得這個group的RouteBuilder,就可以用grouped方法了倔叼,這樣就可以繼續(xù)給group增加子請求了汗唱。
如下面的代碼:
let testGroup = grouped("testGroup")
testGroup.get("extra") { request in
return "testGroup extra result"
}
- Middleware
中間件的請求集,官方給出的例子是auth丈攒,引入AuthProvider到項目即可使用auth中間件
drop.group(AuthMiddleware()) { authorized in
authorized.get("token") { request in
// has been authorized
}
}
這個例子告訴我們哩罪,我們可以使用中間件來處理請求,如使用auth中間件來校驗token巡验,驗證數(shù)據(jù)等识椰。
-
Host
使用host可以指定某個主機的請求,做分布式的同學你懂的深碱。
官方給出的例子:
drop.group(host: "vapor.codes") { vapor in
vapor.get { request in
// only responds to requests to vapor.codes
}
}
-
Chaining
因為grouped方法返回的是RouteBuilder,意味著可以連續(xù)調(diào)用grouped,可以將一系列的group串聯(lián)起來
官方的例子:
drop.grouped(host: "vapor.codes").grouped(AuthMiddleware()).group("v1") { authedSecureV1 in
// add routes here
}
RouteCollection
RouteCollection是一個協(xié)議, 代碼很簡單藏畅,只有一個方法需要實現(xiàn):
public protocol RouteCollection {
func build(_ builder: RouteBuilder) throws
}
如果對上面的group已經(jīng)理解的同學這里就不難理解了敷硅,這里提供了一個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同時實現(xiàn)了RouteCollection 和 EmptyInitializable協(xié)議,并添加了空的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)
}
}
代碼是不是又簡潔了許多幽七?
路由的基本使用就介紹到這里景殷,如果有遺漏或者不清楚的地方請?zhí)嵝盐已a充,希望能對你有所幫助澡屡。
國際慣例猿挚,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)
希望你對我的教程能夠喜歡驶鹉,你們的贊是我持續(xù)的動力绩蜻,歡迎加入QQ群參與互動:431296189