如何从自定义指令中抛出异常?

时间:2013-06-27 21:59:42

标签: spray

我的服务的所有API调用都是HTTP POST,参数在多部分正文中传递。目前我的身份验证看起来像这样

formField("token".as[String]) { token =>
   authorize(isAuthorized(token)) {
      .... my other routes here
   }
}

但它看起来太冗长了。理想情况下,我希望有类似的东西:

myAuthorization {
   .... my other routes here
}

所以我这样写:

def myAuthorization: Directive0 = 
  formField("token".as[String]).require(isAuthorized(_))

但您如何撰写myAuthorization,以便在请求中抛出AuthorizationFailedRejection但没有token

2 个答案:

答案 0 :(得分:6)

我喜欢用提取器编写喷雾指令。我的一个项目的简化示例:

def loggedInUser: Directive[Option[User] :: HNil] = headerValue {
    case Cookie(cookies) => cookies.find(_.name === usrCookie) ∘ extractUserFromCookie
    case _ => None
} | reject(NoUserLoggedInRejection)

def authOpt(usrOpt: Option[User], usr: String): Directive0 =
    authorize(usrOpt.filter(_.login ≠ usr).isEmpty)

然后在您的路由文件中,您可以使用提取器:

pathPrefix("path") {
    loggedInUser { user =>
      authOpt(user, Clients.client) {
        path("path") {
          get {
            complete {
              html.page() // i'm using Twirl as a fe engine
            }
          }
        }
      }
    }

如果没有用户登录,那么它会抛出一个单独处理的拒绝:

implicit val customRejectionHandlers = RejectionHandler {
  case NoUserLoggedInRejection :: _ =>
    ctx => ctx.redirect("/auth", StatusCodes.SeeOther)
}

在安全示例的情况下,这不是最好的,但我认为这很明显

<强>加

评论中的headerValue指令示例:

// Lets define a extractor for the Host header from the request
val hostExtractor: HttpHeader => Option[String] = {
  case Host(host, port) => Some(host)
  case _ => None
}

// Then some path
val route = {
  path("somePath") {
    post {
      headerValue(hostExtractor) { host =>
        complete(host)
      }
    }
  }
}

大多数此类指令都是通过extract指令实现的,该指令采用类型为RequestContext ⇒ T的函数并返回T。例如headerValue也通过extract实现:

extract(_.request.headers.mapFind(protectedF)).flatMap {
  case Some(Right(a))        ⇒ provide(a)
  case Some(Left(rejection)) ⇒ reject(rejection)
  case None                  ⇒ reject
}

答案 1 :(得分:0)

实际上我意识到丢失表单字段错误可能会误导客户端。如果在数据库中找不到给定的令牌,这就是我如何返回auth拒绝

def myAuthorization = formField(FieldDefMagnet("token".as[String])).flatMap[User :: HNil]((token: String) => {
  getUserByToken(token) match {
    case Some(user) => provide(user)
    case None => reject(AuthorizationFailedRejection)
  }
})

我希望它可以帮助其他人。