傳統(tǒng)的Web框架通常都是使用字符串作為路由參數(shù)名稱和參數(shù)類型西采,這就為路由出錯埋下了隱患美尸。 Vapor利用Swift閉包捣染,提供了更加安全骄瓣、更加直觀的訪問路由參數(shù)的方法。
Type Safe
創(chuàng)建一個類型安全的路由耍攘,只需要使用Type
來替換路徑中的一部分:
drop.get("users", Int.self) { request, userId in
return "You requested User #\(userId)"
}
上面創(chuàng)建了一個匹配users/:id
的路由榕栏,:id
是Int
類型下面。作為對比少漆,下面來演示如何手動判斷路由參數(shù)的寫法:
drop.get("users", ":id") { request in
guard let userId = request.parameters["id"]?.int else {
throw Abort.badRequest
}
return "You requested User #\(userId)"
}
我們看到類型安全的路由不僅少了3行代碼臼膏,還可以避免發(fā)生運行時的錯誤(比如:id
拼寫錯誤)硼被。
String Initializable
所有遵守StringInitializable
協(xié)議的類型都可以作為類型安全路由的參數(shù)示损,默認包含以下類型:
- String
- Int
- Model
String
是最常用的,并且總是可以匹配的嚷硫。Int
只會匹配可以轉(zhuǎn)換為整數(shù)的字符串检访,Model
只會匹配通過字符串標識可以在數(shù)據(jù)庫查到的模型。
之前的示例可以更加簡化:
drop.get("users", User.self) { request, user in
return "You requested \(user.name)"
}
這里提供的標識符會被用于自動查找用戶仔掸。比如請求了/users/5
脆贵,User
模型會自動查找標識為5的user
,如果查找到了則請求成功起暮,并且執(zhí)行閉包卖氨,否則會拋出為查找到用戶的異常。
下面是model沒有遵守StringInitializable
協(xié)議的情況下的寫法:
drop.get("users", Int.self) { request, userId in
guard let user = try User.find(userId) else {
throw Abort.notFound
}
return "You requested User #\(userId)"
}
類型安全的路由總共可以為每一個路由減少6行代碼负懦。
Protocol
自定義類型遵守StringInitializable
十分簡單:
// StringInitializable的定義
public protocol StringInitializable {
init?(from string: String) throws
}
Model類實現(xiàn)協(xié)議方法:
extension Model {
public init?(from string: String) throws {
if let model = try Self.find(string) {
self = model
} else {
return nil
}
}
}
init
既可以返回nil
筒捺,也可以拋出異常。這樣你可以拋出自定義的異常纸厉,也可以通過返回nil
使用默認的方法處理異常系吭。
Limits
目前類型安全的路由限制只能有三個參數(shù),可以通過使用group
來解決這個問題颗品。
drop.group("v1", "users") { users in
users.get(User.self, "posts", Post.self) { request, user, post in
return "Requested \(post.name) for \(user.name)"
}
}
上面是處理/v1/users/:userId/posts/:postId
路由請求的結(jié)果肯尺。如果你呼吁增加更多類型安全路由,我們可以考慮將限制數(shù)提高一些躯枢。
Manual
綜上所述则吟,你依然可以使用傳統(tǒng)的路由(非類型安全的路由)來處理特別復雜的情況。
drop.get("v1", "users", ":userId", "posts", ":postId", "comments", ":commentId") { request in
let userId = try request.parameters.extract("userId") as Int
let postId = try request.parameters.extract("postId") as Int
let commentId = try request.parameters.extract("commentId") as Int
return "You requested comment #\(commentId) for post #\(postId) for user #\(userId)"
}
request.parameters
屬性用來提取URI path編碼的參數(shù)(比如/v1/users/1
具有參數(shù):userId
锄蹂,等于"1")氓仲。參數(shù)作為查詢的一部分進行傳遞的情況下(e.g. /v1/search-user?userId=1
),應該使用request.data
(e.g. let userId = request.data["userId"]?.string
)。
除了返回可選值外寨昙,請求參數(shù)可以作為字典來訪問讥巡,也可以使用extract
語法訪問。
Groups
手動使用參數(shù)也適用于group
let userGroup = drop.grouped("users", ":userId")
userGroup.get("messages") { req in
let user = try req.parameters.extract("userId") as User
}
<b>總結(jié):</b>本章主要介紹了路由參數(shù)設置的方法舔哪,類型安全的路由可以極大的簡化我們的代碼欢顷,但是具有最多3個參數(shù)的限制。通過使用group
可以解決這個問題捉蚤。處理復雜的情況下抬驴,我們依然可以使用普通的路由模式。