外部Web APIアクセス

今回は``zio-http``のクライアントについて見ていきます。
[前章](https://qiita.com/wc_sangen/private/63d3d3957245b90d2012)で作成したAPIを利用しつつ、確認していきます。
GETメソッドの場合とPOSTメソッドの場合の二例を具体的に見ていきますが、まず最初にGETメソッドの場合です。

# GETメソッド

実処理部分に対して追加と修正を行います。

```scala:ApplicationService.scala
trait ApplicationService {
(中略)
  def getApi: ZIO[Any, Throwable, Response]
}

object ApplicationService {
(中略)
  def getApi: ZIO[ApplicationService, Throwable, Response] = {
    ZIO.serviceWithZIO[ApplicationService](_.getApi)
  }
}
```
```scala:ApplicationServiceImpl.scala
case class ApplicationServiceImpl(client: Client) extends ApplicationService {
(中略)

  override def getApi: ZIO[Any, Throwable, Response] = for{
    uri <- ZIO.fromEither(URL.decode("http://localhost:8080/text"))
    res <- client.request(
      Request
        .default(
          Method.GET,
          uri,
          Body.empty
        )
    )
    data <- res.body.asString
  } yield Response.text(s"http://localhost:8080/text:$data")
}
object ApplicationServiceImpl {
  val layer: ZLayer[Client, Nothing, ApplicationService] =
    ZLayer{for {
      client <- ZIO.service[Client]
    } yield ApplicationServiceImpl(client)}

}
```
この実処理を呼び出すパスを追加します。
```scala:TestController.scala
object TestController {
  def apply(): Http[ApplicationService, Throwable, Request, Response] = Http.collectZIO[Request] {
(中略)
    case Method.GET -> Root / "getApi"          => ApplicationService.getApi
  }
}
```
最後にHTTPクライアントをDIします。

```scala:MainApp.scala
object MainApp extends ZIOAppDefault {
(中略)
      .provide(
        Server.default,
        ApplicationServiceImpl.layer,
        Client.default
      )
}
```

上記を実行し、``http://localhost:8080/getApi``にアクセスすると以下の出力を得られます。

```shell
http://localhost:8080/text:Hello World!
```

これは実際にHTTPリクエストを行い、得られた値を表示しています。

コードの内容について詳しく見ていきます。

## HTTPクライアントのDI

まず最初にHTTPリクエストを行うに当たってクライアントを準備する必要があります。
そして、準備したクライアントを以下の部分でDIしています。

```scala:MainApp.scala
object MainApp extends ZIOAppDefault {
(中略)
      .provide(
        Server.default,
        ApplicationServiceImpl.layer,
        Client.default
      )
}
```

今回HTTPクライアントに対して特別な設定を行う必要がないため、最も基本的な``Client.default``を使っていますが、プロキシなどの設定を行うことも可能です。
このDIのため、``ApplicationService``,``ApplicationServiceImpl``に対しても修正を行っています。

## GETリクエスト

リクエストを行うためにまず文字列のURLをURL型に変換します。
``URL.decode``関数の返り値の型がEither型であるため、``ZIO.fromEither``関数でZIO型に変換しています。
エラー発生時を考え、ZIO型に直しています。

```scala:ApplicationServiceImpl.scala
uri <- ZIO.fromEither(URL.decode("http://localhost:8080/text"))
```

そして、変換したURLを使い、実際のリクエストを行います。

```scala:ApplicationServiceImpl.scala
    res <- client.request(
      Request
        .default(
          Method.GET,
          uri,
          Body.empty
        )
    )
```

後にも説明しますが、例えば``Method.GET``の部分を``Method.POST``に変えることでPOSTリクエストを行うことが出来たりと、リクエストの条件を変えることもできます。

最後に以下の部分でHTTPレスポンスのボディを取得しています。

```scala:ApplicationServiceImpl.scala
    data <- res.body.asString
```

他にも``res.status.text``によりレスポンスステータスコードを取得するといったことが出来ます。

# POSTメソッド

次にPOSTメソッドについて見ていきます。
最初に実処理部分に対して追記を行います。

```scala:ApplicationService.scala
trait ApplicationService {
(中略)
  def postApi(param:String): ZIO[Any, Throwable, Response]
}

object ApplicationService {
(中略)
  def postApi(param:String): ZIO[ApplicationService, Throwable, Response] = {
    ZIO.serviceWithZIO[ApplicationService](_.postApi(param))
  }
}
```
```scala:ApplicationServiceImpl.scala
case class ApplicationServiceImpl(client: Client) extends ApplicationService {
(中略)
  override def postApi(param:String): ZIO[Any, Throwable, Response] = for{
    uri <- ZIO.fromEither(URL.decode("http://localhost:8080/postTest"))
    res <- client.request(
      Request
        .default(
          Method.POST,
          uri,
          Body.fromString(
            s"userName=$param"
          )
        )
    )
    data <- res.body.asString
  } yield Response.text(s"http://localhost:8080/postTest:$data").setHeaders(Headers("Content-Type", "text/plain;charset=UTF-8")) // 文字化け対策をしています
}
```

パスを追加します。
```scala:TestController.scala
object TestController {
  def apply(): Http[ApplicationService, Throwable, Request, Response] = Http.collectZIO[Request] {
(中略)
    case Method.GET -> Root / "postApi" / test       => ApplicationService.postApi(test)
  }
}
```

上記の修正を行った後実行し、``http://localhost:8080/postApi/test``にアクセスると以下の出力を得られます。

```shell
http://localhost:8080/postTest:あなたの名前はtestです。
```

``http://localhost:8080/postApi/test``の末尾の``test``の部分を変えると出力も変わるので確認してみてください。

## POSTパラメータ

基本的にPOSTリクエストの場合もGETリクエストと変わりません。
POSTパラメータの渡し方の一例として今回は以下の部分で渡しています。

```scala:ApplicationServiceImpl.scala
          Body.fromString(
            s"userName=$param"
          )
```

この場合パラメータ名「userName」に対して変数paramの値を渡しています。
``&``で区切ることにより複数のパラメータを渡すこともできます。

# 終わりに

GETリクエスト、POSTリクエストの方法を今回見てきました。
次章では``zio-http``の中のMiddleware機能について見ていきます。
文字化け対策として``.setHeaders(Headers("Content-Type", "text/plain;charset=UTF-8"))``を付加しているResponseがいくつかありますが、このMiddlewareを使い、これを付けなくとも文字化けを起こさないように修正していきたいと思います。

前章:[zio-http](https://qiita.com/wc_sangen/private/63d3d3957245b90d2012)
次章:[Middlewareについて](https://qiita.com/wc_sangen/private/4bece28969a6a87ff9e0)

# 演習
1. 今回のGETメソッド例について実際に動かして挙動を確かめてください。
    - 解答例は以下になります。
    https://github.com/hatuda/zio-practice/tree/CHAPTER7-Get
1. 今回のPOSTメソッド例について実際に動かして挙動を確かめてください。
    - 解答例は以下になります。
    https://github.com/hatuda/zio-practice/tree/CHAPTER7-Post