Diary

Diary

日々学んだことをアウトプットする場として初めてみました

Retrofit & Gson Converter でぬるぽ

以下のようなセットで API をコールしていた時に、『Non-null と思っていたのにぬるぽが発生する』ということが起きてしまいました。

原因

  • API サーバーで api-key が無効の時に status-code 200 (期待値は 400) + エラーメッセージを返してきてた
  • ステータス 200 なので Retrofit は通信成功とみなし、converer (Gson) によるデコードを開始する
    • デコードに失敗(返却値が Null)してもエラーは出ずに処理が進み、どっかでぬるぽ発生

まとめ

- 『JSON ⇒ データクラス』のデコード等では、失敗時にサイレントで null が返ってくるかもしれない
    - return のタイプを Non-null で定義してたのに。。。
  - ライブラリを使う時は注意!
    - 注意しすぎても良さが減るよな。。。

該当コード

ぬるぽ発生部

// getMembers(groupName).members がぬるのため NullPointerException
// こんぱいら(Android Studio)は無知なので気づいていない!!!
val members = repository.getMembers(groupName).members.map { it.toMember() }

Retrofit 定義部

@Module
@InstallIn(SingletonComponent::class)
object DataModule {

    // API
    @Provides
    @Singleton
    fun provideSakamichiApi(): SakamichiApi {
        val okHttpClient = OkHttpClient.Builder()
            ...
            .build()
        return Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .client(okHttpClient)
            // ========== Gson を converter として指定! ==========
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(SakamichiApi::class.java)
    }
    ...
}

interface SakamichiApi {

    @GET("api/v1/members")
    suspend fun getMembers(
        @Query("gn") groupName: String,
        @Query("key") apiKey: String = "",
    ): MembersDto  // ========== ここでは Non-null で data class を定義している! ==========
    ...
}

対応案

結果 2 つ目の、他の converter (Moshi) を使ってデコードさせました。 Moshi には failOnUnknown というのがありそれを利用しました。

  • Gson, Retrofit 側に『Decode に失敗したら例外を投げる』的な設定ができないか確認
    • 多分無理
  • 他の converter だと挙動変わらないかな~~っての試す
  • API の返却値をぬらぶるにして、コンパイラに教えてあげる
    • てっとりばやいが、無駄に Nullable にするのはいややな。。。

おわりに

Android Studio と Gson を信じた私がおろかでした。