routing Rich Utility for HTTP Supporting modern JSON libararies like Circe and Argonaut. Finagleʼs strong functionalities like Filter and Server are stil available
provided pPath uUse :: to compose as an Endpoint uUse :+: to compose as different Endpoints get("div" :: "op1" :: int :: "op2" :: int) GET /div/op1/(int)/op2/(int) get("hello" :: string) :+: post("echo" :: string) GET /hello/(string) POST /echo/(string)
Endpoint#apply pProcess these here to convert to desired output as a response. get( "div" :: "op1" :: int :: "op2" :: int :: paramOption("pretty").as[Boolean] ){ (op1: Int, op2: Int, isPretty: Boolean) => ??? }
hello: Endpoint[String] = get("hello" :: string) { (str: String) => Ok(s"Hello $str!!!") } val helloBar: Endpoint[String] = get("hello" :: "bar") { Ok("bar") } hello :+: helloBar Given GET /hello/bar => “Hello bar!!!” (1) helloBar :+: hello GET /hello/bar => “bar” (2) Match is checked from head to tail in a combination order Given
uDefine implicit Encoder[A] within the scope of toService uImport io.finch.circe._ within the scope of toService case class Res(result: Int) object CalcService { val getDiv: Endpoint[Res] = get("div" :: "op1" :: int :: "op2" :: int) { (op1: Int, op2: Int) => Ok(Res(op1 / op2)) } } object ServerApp extends TwitterServer { import io.finch.circe._ import io.circe.generic.auto._ val endpoints = CalcService.getDiv.toService . . . } Not here… IT’S HERE!!! ※ With Circe, importing io.circe.generic.auto._ This generates Encoder of any case classes !!!
sometimes not convenient… val hello: Endpoint[String] = get("hello" :: string) { who: String => Ok(s"Hello $who") } GET /hello/bar => ”Hello bar” Response value is wrapped with ”” … val helloPlain: Endpoint[Response] = get("helloPlain" :: string) { who: String => val res = Response() res.setContentType("text/plain") res.setContentString(s"Hello $who") Ok(res) } GET /helloPlain/bar => Hello bar To avoid it, turn the response type of Endpoint to Buf or Reponse and set Content-Type
extractors . get("div" :: "op1" :: int :: "op2" :: int.shouldNot("be 0") {_ == 0}) => NotValid will be thrown when provided 0 to the second param Validation codes can be separated from main logic!!! ※Finch responds with BadRequest when NotValid is thrown,
a “handle” clause uWhen some exceptions are not handle, Finch responds with InternalServerError with an empty payload… val getDiv: Endpoint[Res] = get( "div" :: "op1" :: int :: "op2" :: int ) { (op1: Int, op2: Int) => Ok(Res(op1 / op2)) } handle { case ae: ArithmeticException => BadRequest(ae) }
Exception,you have to override Encoder[Exception] that is defined in Finch as a default case class ErrorRes(errorCode: Int, message: String) extends Exception import io.circe.syntax._ import io.finch.circe._ import io.circe.generic.auto._ implicit val errorEncoder: Encoder[Exception] = Encoder.instance { case er: ErrorRes => er.asJson } val endpoints = CalcService.getDiv.toService ※ Itʼs override. If you donʼt specify the case of Finch build-in exceptions like NotValid, NotPresent, NotParsed, etc…) are not handled. In practice you had better care those cases. !