上篇文章帶大家基本瞭解了一下開始一個 Vapor 專案的流程,本篇緊接著來說說在所有 Web 框架中都最關鍵的 “路由”,因為 “路由” 模組在 Web 專案中擔任很重要的角色,所以很多語言的 Web 框架都把 “路由” 抽離到框架層,從而減少開發者的工作量,一個設計得易用強大的 “路由” 系統也會給相應給框架增添不少色彩。
Web 開發中的路由這個概念簡單來說就是 URL 路徑到具體處理函式之間的對映,只有設定好了路由,訪問者才能在瀏覽器根據相關 URL 規則進行頁面跳轉和訪問,Vapor 對路由做了很多實用性設計,包括路由的構建、路由組、型別安全的路由引數、路由集合等等,希望看完本篇文章你能用 Vapor 寫出一些簡單的路由,我們先來看看 Vapor 最簡單的路由註冊。
Droplet
註冊路由之前我們需要知道 Droplet
這個類,每個程式都應該有一個它的例項,控制著整個程式的生命週期,之後我們會通過 droplet 來註冊路由,新增 provider,新增中介軟體 (middleware) 等等。 droplet 的初始化很簡單,一個空程式看起來就像這樣:
import Vapor
let drop = Droplet()
// your magic here
drop.run()複製程式碼
註冊路由
註冊一個最基本的路由通過對全域性 Droplet
物件呼叫一個方法,指定路徑和一個閉包來接收操作處理。
drop.get("welcome") { request in
return "Hello"
}複製程式碼
我們通過呼叫 get()
方法來註冊了一個路徑為 /welcome
的路由,並返回了 “Hello” 這個字串到瀏覽器,當然我們除了 get
還可以其他的標準 HTTP 方法,比如 post
、 put
、 patch
、 delete
、options
。
另外我們還可以使用 add()
方法來註冊路由,以接收第一個引數 Method
作為 HTTP 方法來動態註冊路由,Method
是一個列舉,包含了上述所列的 HTTP 標準方法,程式碼看起來是這樣:
drop.add(.trace, "welcome") { request in
return "Hello"
}複製程式碼
可能你會想明明上面已經提供了對應的方法來註冊路由,為什麼還要多一個 add()
方法來註冊路由?因為這個方法可以動態註冊,並且支援一些其他不常見的方法(比如 trace
)。
另外有一種關於多級路徑的寫法,直接使用引數分割,而不是在一個 String 引數中用 /
分開,官方推薦是這種寫法,因為可以更容易寫出型別安全的路由引數。
drop.get("foo", "bar", "baz") { request in
return "You requested /foo/bar/baz"
}複製程式碼
如果想在 URL 路由中使用萬用字元怎麼辦?
app.get("anything", "*") { request in
return "Matches anything after /anything"
}複製程式碼
像這個例子因為用了 *
尾隨引數,可以匹配到如下所有路徑:
- /anything
- /anything/foo
- /anything/foo/bar
- /anything/foo/bar/baz
Request
每個路由的閉包都會有一個 Request
引數,用來獲得每一個訪問請求的相關內容,比如 URL 引數、HTTP Header、HTTP Body 等等,而且 Vapor 都已經為你封裝好了很方便的介面來獲取這些內容,甚至直接解析 JSON。
詳細使用可以參考官方文件 Request 一節。
路由引數
Vapor 提倡使用型別安全的路由引數來接收資料,我們可以在路由方法中使用 Swift 型別來指定引數型別,Vapor 會在內部解析並將引數返回給閉包以供使用,非常方便。
drop.get("users", Int.self) { request, userId in
return "You requested User #(userId)"
}複製程式碼
Swift 中處處有協議,路由引數也是如此,我們所見例子中的 Int
其實就是 Vapor 給實現了 StringInitializable
協議,當然 String
也已經預設實現。
public protocol StringInitializable {
init?(from string: String) throws
}複製程式碼
Response
每個路由的閉包中可以返回三種型別的內容,Response
、ResponseRepresentable
、throw
,你可以你可以返回自己所需的 HTTP 狀態碼、URL 重定向、JSON等,基本涵蓋日常所需的請求返回。
Response
Response
是 Vapor 中 HTTP 模組中定義的基於 Message 的類,有很多構造方法方便我們自定義 response 返回:
// 重定向
Response(redirect: "http://vapor.codes")
// JSON
Response(status: .ok, json: JSON(["hello":"world"]))
// String
Response(body: "hello")複製程式碼
ResponseRepresentable
ResponseRepresentable
是一個協議,任何遵循這個協議的物件均可在路由中返回,就像之前例子中我們直接返回了字串,就是因為 Vapor 預設給 String
實現了 ResponseRepresentable
協議,讓我們可以方便的在閉包中直接返回字串,類似的還有 JSON
、Model
物件。
throw
另外一大特性就是可以直接在路由中丟擲異常,我們可以 throw 任何遵從 Swift.Error
協議的物件,當然 Vapor 已經為我們封裝好了幾個常用的 Error 來方便我們丟擲異常。
drop.get("404") { request in
throw Abort.notFound
}複製程式碼
當我們請求這個地址的時候一般會看到一個 Vapor 預設提供的錯誤頁面,還挺漂亮的,如果不想用 Vapor 提供的預設錯誤頁面,我們可以從 drop.middleware
中移除 AbortMiddleware
並新增自己的實現即可。
Abort 列舉在 Vapor 中定義如下:
public enum Abort: Swift.Error {
case badRequest
case notFound
case serverError
case custom(status: Status, message: String)
}複製程式碼
Status 列舉了幾十個我們可能用到的 HTTP 狀態碼,如 200(.ok
)、 301(.movedPermanently
)、403(.forbidden
) …
路由組
Vapor 提供了路由組的概念,通常用來集中組織一組相同字首,新增中介軟體,限制主機名,或者集中管理的路由,路由組有兩個型別:Group
和 Grouped
。
Group 通過一個閉包來收納旗下所有的路由,讓它們有統一的路徑字首,示例如下:
drop.group("v1") { v1 in
v1.get("users") { request in
// get the users
}
}複製程式碼
Grouped 原理類似,只是形式上有所變化,通過 drop.grouped()
方法返回一個 RouteGroup
物件來收納路由。
let v1 = drop.grouped("v1")
v1.get("users") { request in
// get the users
}複製程式碼
文章到此關於 Vapor 路由基本的內容也差不多都介紹完畢了,當然這裡講的可能並不全面,示例程式碼基本來自於官方文件(感謝 ?),下一篇準備說說 Vapor 的模版引擎 Leaf。
之前開的坑在寫一個部落格程式 NSPress,如果大家有興趣歡迎討論。