akka-http 验证路径段

akka-http validate path segment

如何验证 PathMatcher 中的 akka-http 路径段? 我想拒绝请求(而不是简单地将其标记为 Unmatched)。

我想要实现的是 return 400 Bad request if SegmentAsUserId marks segment as invalid:

path(SegmentAsUserId) { implicit userId =>
  concat(
    get {
      handleGet()
    },
    post {
      handlePost()
    }
  )
}

我目前找到的唯一方法是在 PathMatcher1 中抛出异常:

object SegmentAsUserId extends PathMatcher1[String] {
  override def apply(path: Path): PathMatcher.Matching[Tuple1[String]] = path match {
    case Path.Segment(segment, tail) =>
      if (ObjectId.isValid(segment))
        Matched(tail, Tuple1(segment))
      else
        throw InvalidUserIdException(segment)
    case _                           => Unmatched
  }
}

还有一个抛出异常的丑陋解决方案:

case class FindByIdRequest(id: String) {
  require(ObjectId.isValid(id), "The id " + id + " is invalid")
}

path(Segment).as(FindByIdRequest) { implicit userId =>
  // ...
}

我知道可以使用指令(拒绝)。但是有路径匹配的机制吗?

更新:

The solution I came up with.

我不确定 PathMatchers 是您的用例所需要的。来自Akka官方The PathMatcher DSL:

The PathMatcher mini-DSL is used to match incoming URL’s and extract values from them. It is used in the path directive.

它不用于检查这些值的有效性。所以我认为这不是你需要的。

那里有一个很好的例子说明如何使用 PathMatcher

val matcher: PathMatcher1[Option[Int]] =
  "foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create")

val route: Route =
  path(matcher) { i: Option[Int] =>
    complete(s"Matched X${i.getOrElse("")}")
  }

为了实现检查验证,请考虑以下路线:

var user = "none"

object ObjectId {
  def isValid(s: String): Boolean = s.startsWith("a")
}

val complexRoute: Route = path(Segment) { userId =>
  if (ObjectId.isValid(userId)) {
    get {
      complete(StatusCodes.OK, s"userId is: $user")
    } ~
    post {
      user = userId
      complete(StatusCodes.OK)
    }
  } else {
    complete(StatusCodes.BadRequest, s"userId $userId is not supported.")
  }
}

如果从@TomerShetah 的回答中看不出指令可以重复使用,你可以这样做:

object ValidatedObjectId {
  
  def apply(directive: Directive[String]): Directive[String] =
    directive.filter(
      id => ObjectId.isValid(id), // or just ObjectId.isValid
      rejectionObject // e.g. MalformedPathParamRejection("message")
    )
}


val complexRoute: Route = ValidatedObjectId(path(Segment)) { userId =>
  get {
    complete(StatusCodes.OK, s"userId is: $user")
  } ~
  post {
    user = userId
    complete(StatusCodes.OK)
  }
}