傳統(tǒng)的Web框架通常都是使用字符串作為路由參數(shù)名稱和參數(shù)類型,這就為路由出錯(cuò)埋下了隱患。 Vapor利用Swift閉包,提供了更加安全、更加直觀的訪問路由參數(shù)的方法。
Type Safe
創(chuàng)建一個(gè)類型安全的路由,只需要使用Type來替換路徑中的一部分:
drop.get("users", Int.self) { request, userId in
return "You requested User #\(userId)"
}
上面創(chuàng)建了一個(gè)匹配users/:id的路由,:id是Int類型下面。作為對(duì)比,下面來演示如何手動(dòng)判斷路由參數(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ā)生運(yùn)行時(shí)的錯(cuò)誤(比如:id拼寫錯(cuò)誤)。
String Initializable
所有遵守StringInitializable協(xié)議的類型都可以作為類型安全路由的參數(shù),默認(rèn)包含以下類型:
- String
- Int
- Model
String是最常用的,并且總是可以匹配的。Int只會(huì)匹配可以轉(zhuǎn)換為整數(shù)的字符串,Model只會(huì)匹配通過字符串標(biāo)識(shí)可以在數(shù)據(jù)庫查到的模型。
之前的示例可以更加簡(jiǎn)化:
drop.get("users", User.self) { request, user in
return "You requested \(user.name)"
}
這里提供的標(biāo)識(shí)符會(huì)被用于自動(dòng)查找用戶。比如請(qǐng)求了/users/5,User模型會(huì)自動(dòng)查找標(biāo)識(shí)為5的user,如果查找到了則請(qǐng)求成功,并且執(zhí)行閉包,否則會(huì)拋出為查找到用戶的異常。
下面是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)"
}
類型安全的路由總共可以為每一個(gè)路由減少6行代碼。
Protocol
自定義類型遵守StringInitializable十分簡(jiǎn)單:
// StringInitializable的定義
public protocol StringInitializable {
init?(from string: String) throws
}
Model類實(shí)現(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使用默認(rèn)的方法處理異常。
Limits
目前類型安全的路由限制只能有三個(gè)參數(shù),可以通過使用group來解決這個(gè)問題。
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路由請(qǐng)求的結(jié)果。如果你呼吁增加更多類型安全路由,我們可以考慮將限制數(shù)提高一些。
Manual
綜上所述,你依然可以使用傳統(tǒng)的路由(非類型安全的路由)來處理特別復(fù)雜的情況。
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ù)作為查詢的一部分進(jìn)行傳遞的情況下(e.g. /v1/search-user?userId=1),應(yīng)該使用request.data(e.g. let userId = request.data["userId"]?.string)。
除了返回可選值外,請(qǐng)求參數(shù)可以作為字典來訪問,也可以使用extract語法訪問。
Groups
手動(dòng)使用參數(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ù)設(shè)置的方法,類型安全的路由可以極大的簡(jiǎn)化我們的代碼,但是具有最多3個(gè)參數(shù)的限制。通過使用group可以解決這個(gè)問題。處理復(fù)雜的情況下,我們依然可以使用普通的路由模式。