Xiaohe Dong
Xiaohe Dong

Reputation: 5023

how to bind request body in Finch

Here is the code to bind request param to the router.

val testReader: Endpoint[Test] = Endpoint.derive[Test].fromParams
val test: Endpoint[String] = post("test" ? testReader) { t : Test => {
    Created("OK")
  }}

I am using the method fromParams. This method can bind request parameters in a very cool way. However, I dont konw which similiar way I can bind request body in the finch

Many thanks in advance

Upvotes: 7

Views: 2125

Answers (1)

Travis Brown
Travis Brown

Reputation: 139058

For the sake of providing a complete working example I'll assume a case class like this:

case class Test(foo: Int, bar: String)

And some requests like this:

import com.twitter.finagle.http.{ Method, Request, RequestBuilder }
import com.twitter.io.{ Buf, Reader }

val queryParamPost = Request(Method.Post, "/test?foo=1&bar=whatever")

val testJsonBuf = Buf.Utf8("""{ "foo": 1, "bar": "whatever" }""")

val bodyPost = RequestBuilder().url("http://localhost:8080/test").buildPost(testJsonBuf)

Now when you write the following…

import io.finch._

val testParams: Endpoint[Test] = Endpoint.derive[Test].fromParams
val test: Endpoint[Test] = post("test" ? testParams) { test: Test =>
  Created(test)
}

What's happening is that Finch is using generic derivation (powered by Shapeless) to determine (at compile time) how to parse the query params as a Test. You can then test the endpoint like this:

import io.finch.circe._
import io.circe.generic.auto._

test.toService.apply(queryParamPost).onSuccess { response =>
  println(s"$response: ${ response.contentString }")
}

Which will print:

Response("HTTP/1.1 Status(201)"): {"foo":1,"bar":"whatever"}

Here I'm using Circe's generic derivation to automatically encode the "created" Test as JSON for the response.

You can also use Circe to derive a reader for the request body:

val testBody: Endpoint[Test] = body.as[Test]
val test2: Endpoint[Test] = post("test" :: testBody) { test: Test =>
  Created(test)
}

This is almost exactly the same as test above, but we're using body to get an Endpoint[String] that will read the request body and then as to specify that we want the content parsed as JSON and decoded as a Test value. We can test this new version like this:

test2.toService.apply(bodyPost).onSuccess { response =>
  println(s"$response: ${ response.contentString }")
}

And we'll get the answer we expect again.

In general when you want to read a certain kind of information of an incoming request, you'll use one of the basic Endpoints provided by Finch (see the docs for a more complete list), and then use methods like as, map, etc. on the Endpoint to turn it into the shape you need.

Upvotes: 9

Related Questions