为什么Finch用EndPoint来表示Router、Request Parameter和Request Body

Why Finch using EndPoint to represent Router, Request Parameter and Request Body

在finch中,我们可以这样定义路由、请求参数、请求体。

  case class Test(name: String, age: Int)

  val router: Endpoint[Test] = post("hello") { Ok(Test("name", 30)) }
  val requestBody: Endpoint[Test] = body.as[Test]
  val requestParameters: Endpoint[Test] = Endpoint.derive[Test].fromParams

好处是我们可以一起组合EndPoint。例如,我可以定义:

请求路径为hello and Parameter should have name and 年龄。 (router :: requestParameters)

但是,我仍然可以运行一个无效端点,它不包含任何请求路径成功(实际上没有编译错误)

Await.ready(Http.serve(":3000", requestParameters.toService)) 

结果返回 404 未找到页面。即使我希望错误应该像编译错误一样早点报告。我想知道这是设计缺陷还是实际上是 finch 试图修复的?

非常感谢

首先,非常感谢您提出这个问题!

让我向您介绍一下 Finch 端点的工作原理。如果你讲范畴论,Endpoint 是一个 Applicative 嵌入 StateT 表示为接近 Input => Option[(Input, A)].

的东西

简单地说,一个端点采用 Input 包装 HTTP 请求,并捕获 当前路径 (例如:/foo/bar/baz)。当端点应用于给定请求并且匹配它(返回Some)或失败(返回None).匹配时,它会更改 Input 的状态,通常会从中删除第一个路径段(例如:从 /foo/bar/baz 中删除 foo),因此下一个端点是链可以使用新 Input(和新路径)。

一旦端点匹配,Finch 会检查 Input 中是否还有其他不匹配的内容。如果有遗漏,则认为匹配不成功,您的服务 returns 404.

scala> val e = "foo" :: "bar"
e: io.finch.Endpoint[shapeless.HNil] = foo/bar

scala> e(Input(Request("/foo/bar/baz"))).get._1.path
res1: Seq[String] = List(baz)

当涉及到端点 matching/extracting 查询字符串参数时,那里没有路径段被触及,状态被传递到下一个端点不变。因此,当应用端点 param("foo") 时,路径不受影响。这只是意味着,服务查询字符串端点(注意:仅提取查询字符串参数的端点)的唯一方法是向它发送空路径请求/.

scala> val s = param("foo").toService
s: com.twitter.finagle.Service[com.twitter.finagle.http.Request,com.twitter.finagle.http.Response] = <function1>

scala> s(Request("/", "foo" -> "bar")).get
res4: com.twitter.finagle.http.Response = Response("HTTP/1.1 Status(200)")

scala> s(Request("/bar", "foo" -> "bar")).get
res5: com.twitter.finagle.http.Response = Response("HTTP/1.1 Status(404)")