如何在不使用 Vapor 中间件的情况下读取参数
How to read a parameter in a Vapor middleware without consuming it
我是 Vapor 框架的新手,正在尝试保护多条路线。基本上,我想确保 /campaigns/:id
下的所有路由只有在用户实际有权访问具有该 ID 的特定活动时才能访问。这样我就不能只在 url 中输入任何 ID 并访问任何活动。
现在,我想我应该为此使用一个中间件,而不是将逻辑添加到每个路由(到目前为止已经有 6 个)。这是我到目前为止在 Vapor Discord 上一些友好人士的帮助下得出的结论:
final class CampaignMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> {
let user = try request.requireAuthenticated(User.self)
return try request.parameters.next(Campaign.self).flatMap(to: Response.self) { campaign in
guard try campaign.userID == user.requireID() else {
throw Abort(.forbidden, reason: "Campaign doesn't belong to you!")
}
return try next.respond(to: request)
}
}
}
struct CampaignController: RouteCollection {
func boot(router: Router) throws {
let route = router.grouped("campaigns")
let tokenAuthMiddleware = User.tokenAuthMiddleware()
let guardMiddleware = User.guardAuthMiddleware()
let tokenAuthGroup = route.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: getAllHandler)
tokenAuthGroup.post(CampaignCreateData.self, use: createHandler)
// Everything under /campaigns/:id/*, CampaignMiddleware makes sure that the campaign actually belongs to you
let campaignRoute = tokenAuthGroup.grouped(Campaign.parameter)
let campaignMiddleware = CampaignMiddleware()
let protectedCampaignRoute = campaignRoute.grouped(campaignMiddleware)
protectedCampaignRoute.get(use: getOneHandler)
protectedCampaignRoute.delete(use: deleteHandler)
protectedCampaignRoute.put(use: updateHandler)
// Add /campaigns/:id/entries routes
let entryController = EntryController()
try protectedCampaignRoute.register(collection: entryController)
}
func getAllHandler(_ req: Request) throws -> Future<[Campaign]> {
let user = try req.requireAuthenticated(User.self)
return try user.campaigns.query(on: req).all()
}
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
// ...deleted some other route handlers...
}
这里的问题是中间件是 "eating up" 活动参数 request.parameters.next(Campaign.self)
。因此在 getOneHandler
中,它 也 尝试访问 req.parameters.next(Campaign.self)
,但失败并出现错误 "Insufficient parameters"。这是有道理的,因为 .next
实际上从内部参数数组中删除了该参数。
现在,我如何编写一个使用参数的中间件,而不用完它?我是否需要使用原始值并自行查询 Campaign 模型?或者我可以在使用 .next
后以某种方式重置参数吗?或者还有其他更好的方法来处理 Vapor 3 中的模型授权吗?
嘿嘿,看起来你可以从请求中获取你的 Campaign
而不会像这样丢弃它
guard let parameter = req.parameters.values.first else {
throw Abort(.forbidden)
}
try Campaign.resolveParameter(parameter.value, on: req)
所以你的最终代码可能看起来像
final class CampaignMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> {
let user = try request.requireAuthenticated(User.self)
guard let parameter = request.parameters.values.first else {
throw Abort(.forbidden)
}
return try Campaign.resolveParameter(parameter.value, on: request).flatMap { campaign in
guard try campaign.userID == user.requireID() else {
throw Abort(.forbidden, reason: "Campaign doesn't belong to you!")
}
return try next.respond(to: request)
}
}
}
我是 Vapor 框架的新手,正在尝试保护多条路线。基本上,我想确保 /campaigns/:id
下的所有路由只有在用户实际有权访问具有该 ID 的特定活动时才能访问。这样我就不能只在 url 中输入任何 ID 并访问任何活动。
现在,我想我应该为此使用一个中间件,而不是将逻辑添加到每个路由(到目前为止已经有 6 个)。这是我到目前为止在 Vapor Discord 上一些友好人士的帮助下得出的结论:
final class CampaignMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> {
let user = try request.requireAuthenticated(User.self)
return try request.parameters.next(Campaign.self).flatMap(to: Response.self) { campaign in
guard try campaign.userID == user.requireID() else {
throw Abort(.forbidden, reason: "Campaign doesn't belong to you!")
}
return try next.respond(to: request)
}
}
}
struct CampaignController: RouteCollection {
func boot(router: Router) throws {
let route = router.grouped("campaigns")
let tokenAuthMiddleware = User.tokenAuthMiddleware()
let guardMiddleware = User.guardAuthMiddleware()
let tokenAuthGroup = route.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: getAllHandler)
tokenAuthGroup.post(CampaignCreateData.self, use: createHandler)
// Everything under /campaigns/:id/*, CampaignMiddleware makes sure that the campaign actually belongs to you
let campaignRoute = tokenAuthGroup.grouped(Campaign.parameter)
let campaignMiddleware = CampaignMiddleware()
let protectedCampaignRoute = campaignRoute.grouped(campaignMiddleware)
protectedCampaignRoute.get(use: getOneHandler)
protectedCampaignRoute.delete(use: deleteHandler)
protectedCampaignRoute.put(use: updateHandler)
// Add /campaigns/:id/entries routes
let entryController = EntryController()
try protectedCampaignRoute.register(collection: entryController)
}
func getAllHandler(_ req: Request) throws -> Future<[Campaign]> {
let user = try req.requireAuthenticated(User.self)
return try user.campaigns.query(on: req).all()
}
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
// ...deleted some other route handlers...
}
这里的问题是中间件是 "eating up" 活动参数 request.parameters.next(Campaign.self)
。因此在 getOneHandler
中,它 也 尝试访问 req.parameters.next(Campaign.self)
,但失败并出现错误 "Insufficient parameters"。这是有道理的,因为 .next
实际上从内部参数数组中删除了该参数。
现在,我如何编写一个使用参数的中间件,而不用完它?我是否需要使用原始值并自行查询 Campaign 模型?或者我可以在使用 .next
后以某种方式重置参数吗?或者还有其他更好的方法来处理 Vapor 3 中的模型授权吗?
嘿嘿,看起来你可以从请求中获取你的 Campaign
而不会像这样丢弃它
guard let parameter = req.parameters.values.first else {
throw Abort(.forbidden)
}
try Campaign.resolveParameter(parameter.value, on: req)
所以你的最终代码可能看起来像
final class CampaignMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> {
let user = try request.requireAuthenticated(User.self)
guard let parameter = request.parameters.values.first else {
throw Abort(.forbidden)
}
return try Campaign.resolveParameter(parameter.value, on: request).flatMap { campaign in
guard try campaign.userID == user.requireID() else {
throw Abort(.forbidden, reason: "Campaign doesn't belong to you!")
}
return try next.respond(to: request)
}
}
}