Ktor HttpClientでREST

これまでに Ktor HttpClient を使って [GET](https://qiita.com/keijidosha/items/3bd89f81d41a091c8547) や [POST](https://qiita.com/keijidosha/items/173cd002f3eed7e29374#_reference-2b20335b0cf975590d36) を動かしてみました。
REST API をたたく場合は、それ以外にも PUT や DELETE などが必要になってくると思います。
そこで今回は PUT を動かしてみました。

## PUT
ソースは次のようになります。
build.gradle は [POST](https://qiita.com/keijidosha/items/173cd002f3eed7e29374#_reference-2b20335b0cf975590d36) の場合と同じですので省略します。

```kotlin
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.request.request
import io.ktor.client.response.HttpResponse
import io.ktor.client.response.readText
import io.ktor.http.HttpMethod
import io.ktor.content.OutgoingContent.ReadChannelContent
import io.ktor.cio.readChannel
import kotlinx.coroutines.experimental.runBlocking
import java.io.File

fun main( args: Array<String> ) {
    HttpClient( Apache ).use {
        val response = runBlocking {
            it.request<HttpResponse> {
                url {
                    host = "localhost"
                    port = 8000
                    encodedPath = "/put"
                }
                method = HttpMethod.Put
                body = object: ReadChannelContent() {
                    override fun readFrom() = File( "data.bin" ).readChannel()
                }
            }.use { res ->
                println( res.version )
                println( res.status )
                res.headers.forEach { k, v ->
                    println( "$k: $v" )
                }
                res.readText()
            }
        }

        println( "$response" )
    }
}
```

ktor-client-core の [builders.kt](https://github.com/ktorio/ktor/blob/master/ktor-client/ktor-client-core/src/io/ktor/client/request/builders.kt) には、HttpClient の拡張関数として get(), post() が定義されていますが、それ以外の HTTPメソッドは用意されていませんでした。
そこでこれも HttpClient への拡張関数ですが、request() を使います。(get, post も内部では reuqest を呼んでいました。)

request() の引数は builder: [HttpRequestBuilder](https://github.com/ktorio/ktor/blob/master/ktor-client/ktor-client-core/src/io/ktor/client/request/HttpRequest.kt) です。

### HTTPメソッド
HttpRequestBuilder には method という HttpMethod 型のプロパティがあり、デフォルトは HttpMethod.Get です。

```kotlin
var method = HttpMethod.Get
```
この変数に使いたい HTTPメソッドをセットします。
[HttpMethod](https://github.com/ktorio/ktor/blob/master/ktor-http/src/io/ktor/http/HttpMethod.kt) クラスに用意されている HTTP メソッドは次のようになります。

|メソッド |定義               |
|:------|:-----------------|
|GET    |HttpMethod.Get    |
|POST   |HttpMethod.Post   |
|PUT    |HttpMethod.Put    |
|PATCH  |HttpMethod.Patch  |
|DELETE |HttpMethod.Delete |
|HEAD   |HttpMethod.Head   |
|OPTIONS|HttpMethod.Options|

### 接続先指定
接続先は次のように指定しています。

```kotlin
url {
    host = "localhost"
    port = 8000
    encodedPath = "/put"
}
```
これは [URLBuilder](https://github.com/ktorio/ktor/blob/master/ktor-http/src/io/ktor/http/URLBuilder.kt) のコンストラクタを呼んでいます。パラメーターとデフォルト値は次のように定義されています。

```kotlin
class URLBuilder(
        var protocol: URLProtocol = URLProtocol.HTTP,
        var host: String = "localhost",
        var port: Int = protocol.defaultPort,
        var user: String? = null,
        var password: String? = null,
        var encodedPath: String = "/",
        val parameters: ParametersBuilder = ParametersBuilder(),
        var fragment: String = "",
        var trailingQuery: Boolean = false
) {
```
### HTTPボディ(送信データ)
body には送信するデータを渡します。使い方は [POST](https://qiita.com/keijidosha/items/173cd002f3eed7e29374#_reference-2b20335b0cf975590d36) と同じですので、こちらを参考にしてください。

## HEAD
今回は PUT を使ってみましたが、DELETE や PATCH でも基本的には同じコーディングになると思います。
HEAD も試してみましたので合わせて掲載しておきます。

```kotlin
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.request.request
import io.ktor.client.response.HttpResponse
import io.ktor.client.response.readText
import kotlinx.coroutines.experimental.runBlocking
import io.ktor.http.URLBuilder
import io.ktor.http.HttpMethod
import java.nio.charset.StandardCharsets

fun main( args: Array<String> ) {
    HttpClient( Apache ).use {
        val response = runBlocking {
            it.request<HttpResponse> {
                url { host = "www.example.com" }
                method = HttpMethod.Head
            }.use { res ->
                println( res.version )
                println( res.status )
                res.headers.forEach { k, v ->
                    println( "$k: $v" )
                }
                res.readText()
            }
        }

        println( "body=$response" )
    }
}
```