2 votes

Impossible d'envoyer avec succès une requête POST en utilisant Retrofit 2.6.1 - Problèmes avec le coverter JSON

J'utilise le nouveau Retrofit2 avec suspension des coroutines, et avec les requêtes GET tout fonctionne bien.

Mais je dois maintenant implémenter une requête POST, et je n'arrive pas à la faire fonctionner.

J'ai un exemple CURL qui ressemble à ceci : curl -X POST -H "Content-Type: application/json;charsets: utf-8" -d '{"tx_guapptokenlist_tokenitem":{"tokenchar":"my-token-string","platform":"android"}}' https://www.example-url.com/tokens?type=56427890283537921

Cela fonctionne bien, et renvoie cette réponse : {"errors":false,"success":true}%

Voici donc à quoi ressemble ma requête dans ma classe Api :

    @Headers( "Content-Type: application/json" )
    @POST("/tokens?type=56427890283537921")
    suspend fun sendFirebaseToken(@Body tokenRequest: RequestBody) : Call<TokenResponse>

Voici ma classe TokenResponse :

@JsonClass(generateAdapter = true)
data class TokenResponse(
    @Json(name="errors")
    val errors: Boolean,
    @Json(name="success")
    val success: Boolean)

et la classe ApiClient que j'utilise :

object ApiClient {

    private const val BASE_URL = "https://myExampleUrl.com"
    private var retrofit: Retrofit? = null

    var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
    val client: Retrofit?
        get() {
            if (retrofit == null) {
                retrofit = Retrofit.Builder().baseUrl(
                    BASE_URL
                ).client(getOkHttpClient())
                    .addConverterFactory(MoshiConverterFactory.create())
                    .build()
            }
            return retrofit
        }

    fun getOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(getLoggingInterceptor())
            .connectTimeout(120, TimeUnit.SECONDS)
            .readTimeout(120, TimeUnit.SECONDS).writeTimeout(90, TimeUnit.SECONDS).build()
    }

    private fun getLoggingInterceptor(): HttpLoggingInterceptor {
        return HttpLoggingInterceptor().setLevel(
            if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.HEADERS
            else HttpLoggingInterceptor.Level.NONE
        )
    }
}

La première chose étrange que j'ai remarquée : Même avec le @POST si mon suspend fun n'a pas de type de retour, je n'obtiens pas d'erreur, mais okhttp enverra toujours une demande GET (au moins le point de terminaison reçoit toujours un GET). Je ne sais pas si c'est censé être comme ça ?

De toute façon : J'ai besoin des valeurs de retour, donc je retourne Call<TokenResponse> . Cela m'amène à mon principal problème, que je n'arrive pas à résoudre : Si maintenant j'exécute mon code, il se plante avec ce log :

java.lang.IllegalArgumentException: Unable to create converter for     retrofit2.Call<myapp.communication.TokenResponse>
        for method TokenApi.sendToken
        at retrofit2.Utils.methodError(Utils.java:52)

Pour essayer de résoudre ce problème, j'ai utilisé moshi-kotlin-codegen pour générer l'adaptateur approprié (d'où les annotations dans la classe de données), mais sans succès. La classe est générée, mais n'est pas utilisée. J'ai essayé de passer un Moshi avec JsonAdapterFactory comme ceci var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() à mon ConverterFactory mais cela ne fonctionne pas non plus. J'ai essayé d'ajouter l'adaptateur généré maually à moshi mais cela ne fonctionne pas non plus.

J'ai également essayé de renvoyer différents types dans ma demande. La documentation de Retrofit indique que sans convertisseur, on ne peut renvoyer qu'un type ResponseBody mais le résultat est le même : Retrofit se plaint de ne pas avoir de convertisseur. La même chose pour le retour Call<Void>

J'ai l'impression de manquer quelque chose ici ? Qui peut m'aider ? Je suis heureux de fournir plus de détails, veuillez demander ce dont vous avez besoin.

3voto

Dominic Fischer Points 1155

Votre fonction de demande devrait ressembler à ceci.

@Headers( "Content-Type: application/json" )
@POST("/tokens?type=56427890283537921")
suspend fun sendFirebaseToken(@Body tokenRequest: RequestBody): TokenResponse

Vous n'utilisez pas Call<...> puisque vous l'avez marqué comme suspendu.

1voto

syslogic Points 749

Je pense que l'annotation devrait être :

@JsonClass(generateAdapter = true)
data class TokenResponse(
    @field:Json(name = "errors") val errors: Integer,
    @field:Json(name = "success") val success: Boolean
)

Et essayez d'enlever le suspend une fois, ce qui pourrait entrer en conflit avec generateAdapter = true .

-1voto

michpohl Points 332

J'ai réussi à le faire fonctionner maintenant, voici ce que j'ai appris :

Tout d'abord : @Dominic Fischer aquí a raison, Call est erronée, et si tout est correctement configuré, il n'est pas nécessaire d'envelopper l'objet de résultat du tout (j'ai remarqué par la façon dont l'objet @Headers semble ne pas être nécessaire, Retrofit semble s'en charger).

Le second et plus gros problème est que le client dans mon ApiClient n'a pas été utilisée correctement. Voir la nouvelle version :

fun getRetrofitService(): ApiService {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(getOkHttpClient())
            .addConverterFactory(MoshiConverterFactory.create())
            .build().create(ApiService::class.java)
    }

Vous voyez que maintenant l'étape 'create()' est ajoutée, ce que je gérais auparavant en dehors de cette classe. Là, j'ai utilisé mon Retrofit pour créer le service, comme ici, mais j'ai accidentellement passé l'objet ApiClient::class.java . Il est intéressant de noter que la compilation et l'exécution se déroulent parfaitement, mais bien sûr, il doit y avoir un problème quelque part - il est incapable de construire correctement les adaptateurs JSON.

Par conséquent, j'ai intégré cette étape dans mon ApiClient afin d'éviter de tels accidents à l'avenir.

Si quelqu'un a des suggestions pour rendre cette question + réponse plus utile pour les futurs lecteurs, faites-le moi savoir !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X