Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 699
Xiaohe Dong Avatar asked May 01 '16 12:05

Xiaohe Dong


1 Answers

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.

like image 63
Travis Brown Avatar answered Nov 20 '22 14:11

Travis Brown