iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0

因為 API first, RESTful Service 的取用是常見的場景。Java 在開發上,有一個難題的是同一個功能性的,會有很多家不同的實作。例如 JSON 有 gson, jsonb, jackson..., 而 HTTP Client 也是百家爭鳴,例如有以下

  • Java 11 HttpClient
  • Apache HttpClient
  • RestTemplate - Spring Boot
  • Ktor Client - Kotlin
  • MicroProfile - Eclipse (現在打出 Eclipse 都覺得自已有點年紀)

Quarkus 生態系中帶的是 Eclipse MicroProfile HTTP Client, 可以用少少設定就把外部的 RESTful Service 當作自家的 service。有點像是 proxy的感覺。Quarkus 有分 Rest Client 與 Reactive Rest Client,不過今天是要利用 Kotlin 的 croutines 把 Rest Client 轉成 Reactive

目標 fruityvice

今天要打的目標是這邊 https://www.fruityvice.com/api/fruit/banana
GET 回來是

{"name":"Orange","genus":"Citrus","id":2,"family":"Rutaceae","order":"Sapindales","nutritions":{"carbohydrates":8,"protein":1,"fat":0.2,"calories":43,"sugar":8.2}}

增加 Dependency

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-rest-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-rest-client-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-rest-client-mutiny</artifactId>
    </dependency>    

建立 FruityVice Data Class

根據 Json 長相建立 class , 也可以利用 JSON To Kotlin Class plugin 作建立

data class FruityVice(
    val name: String,
    val genus: String,
    val id: Long,
    val family: String,
    val order: String,
    val nutritions: Nutrition
)

data class Nutrition(val carbohydrates: Long, val protein: Long, val fat: Double, val calories: Long, val sugar: Double)

建立 FruityVice Interface

這個雖然長的很像 RESTful endpoint , 但是要注意他是 interface, PATH 只會有相對的要打出去的 uri, Host 要在properties 設定。用 Uni 表示 async call


@Path("/api/fruit")
@RegisterRestClient(configKey = "fruits-api")
interface FruityViceService {
    @GET
    @Path("/{name}")
    @Produces(MediaType.APPLICATION_JSON)
    fun getFruitByName(@PathParam("name") name: String): Uni<FruityVice>
}

properties 設定

根據剛剛的 config-key,在 properties 設定 host

quarkus.rest-client.fruits-api.url=https://www.fruityvice.com
quarkus.rest-client.fruits-api.scope=javax.inject.Singleton

FruitService

  1. 以 RestClient 型式注入 fruityViceService
  2. 利用 Uni type 可以達到 function 的 retry
@ApplicationScoped
class FruitService(@RestClient val fruityViceService: FruityViceService) {  // (1)

    suspend fun findByName(name: String) = Either.catch {
        fruityViceService.getFruitByName(name)
            .onFailure().retry().atMost(3).awaitSuspending()
    }.mapLeft {
        when {
            it.message.orEmpty().contains("status code 404") -> AppError.NoThisFruit(name)
            else -> AppError.FruitServiceCallError(it)
        }
    }
}

Proxy REST Endpoint

  1. 注入 fruitService
  2. identity 指的就是自已,相當於 {it}

@Path("/fruits")
class FruitResource(val fruitService: FruitService) { //(1)

    @GET
    @Path("/{name}")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    suspend fun fruit(@PathParam("name") name: String) = fruitService.findByName(name).fold(
        ifRight = ::identity,    //(2)
        ifLeft = { AppError.toResponse(it) }
    )
}

測試 http://localhost:8080/fruits/orange

https://ithelp.ithome.com.tw/upload/images/20221004/20135701tazJkQXhtN.png

其他的參數與設定

https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html

有相關的 method 與進階 header 要如何傳入的設定。

Disabling Hostname /SSL Verification

在封閉的網路環境,需要作一些憑證的全信任,Quarkus 可以直接設定。

quarkus.rest-client.extensions-api.hostname-verifier=io.quarkus.restclient.NoopHostnameVerifier
quarkus.tls.trust-all=true

本日 Sample

https://github.com/hmchangm/getting-start-QK/tree/ironman2022-d26-jdb/src/main

其他的選擇 Ktor Client

Ktor Client 可以參考我們鐵人賽團隊的文章,也是個不錯的選擇
Kotlin 全面啟動 Ktor Client III


上一篇
Quarkus Reactive 資料庫連接 with Kotlin JDBC (no ORM)
下一篇
Quarkus 的各種 Kubernetes 佈署 Azure, AWS, GCP
系列文
Quarkus, Kotlin, Reactive 雲原生服務開發32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言