Невозможно успешно отправить запрос POST с помощью Retrofit 2.6.1 - Проблемы с JSON coverter - PullRequest
1 голос
/ 21 сентября 2019

Я использую новый Retrofit2 с приостановкой сопрограмм, и с запросами GET все работает нормально.

Но теперь мне нужно реализовать запрос POST, и я просто не могу заставить его работать

У меня есть пример CURL, который выглядит так: 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

Это работает нормально и возвращает этот ответ: {"errors":false,"success":true}%

Итак, вот как выглядит мой запрос прямо сейчас в моем классе Api:

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

Это мой класс TokenResponse:

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

и класс ApiClient, который я использую:

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

Первое, что я заметил: даже с аннотацией @POST, если у моего suspend fun нет возвращаемого типа, яне получить ошибку, но okhttp всегда будет отправлять запрос GET (по крайней мере, конечная точка всегда получает GET).Не уверен, что так должно быть?

В любом случае: мне нужны возвращаемые значения, поэтому я возвращаю Call<TokenResponse>.Это приводит меня к моей главной проблеме, которую я не могу решить: если сейчас я выполняю свой код, он вылетает с таким журналом:

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

Чтобы попытаться справиться с этим, я использовал moshi-kotlin-Codegen для генерации правильного адаптера (отсюда и аннотации в классе данных), но безрезультатно.Класс генерируется, но не используется.Я пытался передать Moshi с помощью JsonAdapterFactory, например, var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build(), на мой ConverterFactory, но это тоже не работает.Попытка добавить сгенерированный адаптер вручную в moshi, но это также не сработало.

Я также попытался вернуть различные типы в моем запросе.Документы Retrofit заявляют, что без конвертера можно вернуть только ResponseBody, но результат тот же: Retrofit жалуется, что у него нет конвертера.То же самое для возврата Call<Void>

Мне кажется, что я что-то здесь упускаю?Кто может помочь?Рад предоставить более подробную информацию, пожалуйста, запросите, что нужно.

Ответы [ 3 ]

1 голос
/ 22 сентября 2019

Ваша функция запроса должна выглядеть следующим образом.

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

Вы не используете Call<...>, поскольку отметили ее как приостановленную.

0 голосов
/ 22 сентября 2019

Теперь у меня все работает, вот что я выучил:

Прежде всего: @Dominic Fischer здесь правильно, Call неправильно, и все установленоправильно, нет необходимости оборачивать объект результата вообще (я заметил, что аннотация @Headers выглядит ненужной, Retrofit, кажется, просто позаботится об этом).

Второй иСамая большая проблема в том, что объект client в моем классе ApiClient использовался неправильно.Смотрите новую версию:

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

Смотрите, что теперь добавлен шаг 'create ()', который раньше я обрабатывал вне этого класса.Там я использовал свой Retrofit объект для создания сервиса, как здесь, но я случайно передал ApiClient::class.java.Интересно, что он компилируется и работает просто отлично, но, конечно, это должно где-то испортиться - он не может правильно собрать JSON-адаптеры.

В результате я ввел этот шаг в свой ApiClient, чтобы предотвратить подобные несчастные случаи в будущем.

Если у кого-то есть предложения относительно постановки этого вопроса + ответ более полезен для будущих читателейПожалуйста, дайте мне знать!

0 голосов
/ 21 сентября 2019

Подумайте, аннотация должна быть:

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

И попробуйте удалить ключевое слово suspend один раз, что может привести к конфликту с generateAdapter = true.

...