【问题标题】:How to use string directive extractor in a nested route in Spray如何在 Spray 的嵌套路由中使用字符串指令提取器
【发布时间】:2015-10-25 20:15:54
【问题描述】:

在这里回答我自己的问题,因为我花了一天多的时间才弄清楚,这是一个非常简单的问题,我认为其他人可能会遇到。

在使用 Spray 创建的 RESTful-esk 服务时,我想匹配路径中包含字母数字 id 的路由。这是我最初的开始:

case class APIPagination(val page: Option[Int], val perPage: Option[Int])
get {
  pathPrefix("v0" / "things") {
    pathEndOrSingleSlash {
      parameters('page ? 0, 'perPage ? 10).as(APIPagination) { pagination =>
        respondWithMediaType(`application/json`) {
          complete("things")
        }
      }
    } ~ 
    path(Segment) { thingStringId =>
      pathEnd {
        complete(thingStringId)
      } ~
      pathSuffix("subthings") {
        pathEndOrSingleSlash {
          complete("subthings")
        }
      } ~
      pathSuffix("othersubthings") {
        pathEndOrSingleSlash {
          complete("othersubthings")
        }
      } 
    }
  }
} ~ //more routes...

而且这编译没有问题,但是当使用scalatest验证路由结构是否正确时,我惊讶地发现这种类型的输出:

"ThingServiceTests:"
"Thing Service Routes should not reject:"
- should /v0/things
- should /v0/things/thingId
- should /v0/things/thingId/subthings *** FAILED ***
  Request was not handled (RouteTest.scala:64)
- should /v0/things/thingId/othersubthings *** FAILED ***
  Request was not handled (RouteTest.scala:64)

我的路线有什么问题?

【问题讨论】:

    标签: scala spray spray-dsl


    【解决方案1】:

    我查看了许多资源,like this SO Questionthis blog post,但似乎找不到任何关于使用字符串 Id 作为路由结构的顶层部分的信息。我浏览了spray scaladoc,并在发现this important test (duplicated below)之前用头撞了documentation on Path matchers for a while

    "pathPrefix(Segment)" should {
        val test = testFor(pathPrefix(Segment) { echoCaptureAndUnmatchedPath })
        "accept [/abc]" in test("abc:")
        "accept [/abc/]" in test("abc:/")
        "accept [/abc/def]" in test("abc:/def")
        "reject [/]" in test()
      }
    

    这让我知道了几件事。我应该尝试使用pathPrefix 而不是path。所以我改变了我的路线看起来像这样:

    get {
      pathPrefix("v0" / "things") {
        pathEndOrSingleSlash {
          parameters('page ? 0, 'perPage ? 10).as(APIPagination) { pagination =>
            respondWithMediaType(`application/json`) {
              listThings(pagination)
            }
          }
        } ~ 
        pathPrefix(Segment) { thingStringId =>
          pathEnd {
            showThing(thingStringId)
          } ~
          pathPrefix("subthings") {
            pathEndOrSingleSlash {
              listSubThingsForMasterThing(thingStringId)
            }
          } ~
          pathPrefix("othersubthings") {
            pathEndOrSingleSlash {
              listOtherSubThingsForMasterThing(thingStringId)
            }
          } 
        }
      }
    } ~
    

    很高兴我的所有测试都通过了,并且路由结构正常工作。然后我将其更新为使用 Regex 匹配器:

    pathPrefix(new scala.util.matching.Regex("[a-zA-Z0-9]*")) { thingStringId =>
    

    并决定为遇到类似问题的其他人发布 SO。正如 jrudolph 在 cmets 中指出的那样,这是因为 Segment 期望匹配 <Segment><PathEnd> 而不是在路径中间使用。 pathPrefix 对哪个更有用

    【讨论】:

    • 如果Segment 匹配文字斜线,那将是一个错误。您可能遇到的是path(Segment) 只会匹配/<someSegment><PathEnd>,即末尾的单个段。如果要匹配路径中间的单个段,则需要使用 pathPrefix(Segment)。您引用的测试表明一切都按预期工作:在每个示例中,仅提取了 abc,其余路径保留在 unmatchedPath 中。
    • 或者用你自己的例子来解释一下:/v0/things/thingId/subthings 匹配失败,因为path(Segment) 已经预期thingId 之后的路径结束,而不是因为它消耗了所有路径。
    • 如果 spray 网站上的文档明确说明匹配 <somesegment><pathend> 会很好,因为您需要使用 pathPrefix 而不是 path 一点也不明显。或者至少对我来说不是。
    • @jrudolph 我已经更新了答案,我是否很好地描述了推理?我想确保其他人不会对此感到厌烦(如果可以,请随时自行编辑)
    • 顺便说一句。通过使用pathSuffix,你现在匹配的比需要的多。例如。 /v0/things/<thingId>/abc/def/ghi/subthings 现在也将被匹配。基本规则是:pathPrefix 用于所有外部匹配,pathpathEndpathSingleSlashpathEndOrSingleSlash 仅用于最嵌套的匹配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-04
    • 2022-10-12
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    相关资源
    最近更新 更多