2
votes

Impossible d'envoyer avec succès une demande POST à ​​l'aide de Retrofit 2.6.1 - Problèmes avec le convertisseur JSON

J'utilise le nouveau Retrofit2 avec la 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 tout simplement 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 en ce moment:

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

Ceci est ma classe TokenResponse:

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
        )
    }
}

et la classe ApiClient que j'utilise:

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

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

Quoi qu'il en soit: j'ai besoin des valeurs de retour, donc je renvoie Call . Cela m'amène à mon problème principal, que je ne peux pas résoudre: si maintenant j'exécute mon code, il plante avec ce journal:

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

Pour essayer de résoudre ce problème, je ont utilisé moshi-kotlin-codegen pour générer l'adaptateur approprié (d'où les annotations dans la classe de données), mais en vain. La classe est générée, mais pas utilisée. J'ai essayé de transmettre un Moshi avec JsonAdapterFactory comme celui-ci 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é manuellement à moshi mais cela n'a pas non plus fonctionné.

J'ai également essayé de renvoyer différents types dans ma requête. La documentation Retrofit déclare que sans convertisseur, on ne pourrait renvoyer qu'un ResponseBody , mais le même résultat: Retrofit se plaint de ne pas avoir de convertisseur. La même chose pour le retour de Call

J'ai l'impression de manquer quelque chose ici? Qui peut aider? Heureux de fournir plus de détails, veuillez demander ce qui est nécessaire.


3 Réponses :


1
votes

Pensez 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 de supprimer le mot-clé suspend une fois, ce qui pourrait entrer en conflit avec generateAdapter = true .


2 commentaires

Merci, je vais essayer et faire un rapport! En attendant, j'ai découvert que si je retourne une Response , je n'obtiens plus d'exception, mais encore une fois, le point de terminaison déclare qu'il reçoit une demande GET et que les en-têtes et le corps ont disparu. Il est possible que ce soit mon problème principal, mais je suppose que c'est une question complètement différente alors ...


Il s'avère que tout est correctement configuré, aucune annotation n'est nécessaire. Je suppose que les objets JSON à générer / décoder ici sont si simples que Moshi peut simplement le comprendre. Je les ai supprimés et je n'ai aucun problème.



2
votes

Votre fonction de requête doit ressembler à ceci.

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

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


1 commentaires

C'est juste. Comme ce n'est qu'une partie du problème, je l'ai référencé dans ma réponse où j'explique ce qui ne va pas.



-1
votes

Je l'ai fait fonctionner maintenant, voici ce que j'ai appris:

Tout d'abord: @Dominic Fischer ici a> a raison, Call est faux, et avec tout configuré correctement, il n'est pas du tout nécessaire d'envelopper l'objet de résultat (j'ai remarqué au passage le @Headers l'annotation ne semble pas nécessaire, Retrofit semble s'en occuper).

Le deuxième et plus gros problème est que l'objet client dans mon code ApiClient > la classe 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)
    }

Voyez que maintenant l'étape 'create ()' est ajoutée, ce que j'avais auparavant géré en dehors de cette classe. Là, j'ai utilisé mon objet Retrofit pour créer le service comme ici, mais j'ai accidentellement passé ApiClient :: class.java . Il est intéressant de noter que compile et fonctionne très bien, mais bien sûr, cela doit gâcher quelque part - il est impossible de créer correctement les adaptateurs JSON.

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

Si quelqu'un a des suggestions pour répondre à cette question + réponse plus utile pour les futurs lecteurs, merci de me le faire savoir!


0 commentaires