Foso / Ktorfit

HTTP client generator / KSP plugin for Kotlin Multiplatform (Android, iOS, Js, Jvm, Native, WasmJs)) using KSP and Ktor clients inspired by Retrofit https://foso.github.io/Ktorfit
https://foso.github.io/Ktorfit
Apache License 2.0
1.62k stars 43 forks source link

Problems with body to make a post #91

Closed joseluisgs closed 2 years ago

joseluisgs commented 2 years ago

Hi I have the following Client

object KtorFitClient {
    private const val API_URL = "https://reqres.in/"

    private val ktorfit by lazy {
        // Podemos meterle flow directamente!!!
        // ktorfit.responseConverter(FlowResponseConverter())
        Ktorfit.Builder()
            .baseUrl(API_URL)
            .httpClient {
                install(ContentNegotiation) {
                    json(Json { isLenient = true; ignoreUnknownKeys = true })
                }
            }
            .build()
    }

    // Creamos una instancia de Retrofit con las llamadas a la API
    val instance by lazy {
        ktorfit.create<KtorFitRest>()
    }
}

I have the following interface

interface KtorFitRest {

    @GET("api/users")
    suspend fun getAll(@Query("page") page: Int = 0, @Query("per_page") perPage: Int = 0): GetAllDto

    @GET("api/users/{id}")
    suspend fun getById(@Path("id") id: Int): GetByIdDto

    @POST("api/users")
    suspend fun create(@Body user: User): CreateDto
}

when I make val res = client.create(entity) I have the following exception

Exception in thread "main" java.lang.IllegalStateException: Fail to prepare request body for sending. 
The body type is: class models.User (Kotlin reflection is not available), with Content-Type: null.

If you expect serialized body, please check that you have installed the corresponding plugin(like `ContentNegotiation`) and set `Content-Type` header.
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invokeSuspend(HttpSend.kt:88)
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(HttpSend.kt)
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(HttpSend.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:91)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:126)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invokeSuspend(HttpRequestLifecycle.kt:35)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77)
    at io.ktor.client.HttpClient.execute$ktor_client_core(HttpClient.kt:191)
    at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:108)
    at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:47)
    at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:62)

I don't Know why, Because gets work fine. My user model is

@Serializable
data class User(
    val id: Int = 0,
    val first_name: String,
    val last_name: String,
    val avatar: String,
    val email: String,
)

Could you help me? I try to solve using @Headers("Content-Type: application/json"), but I have installed the context serializer pluging,

Thank you

joseluisgs commented 2 years ago

OK, I solved with

private val ktorfit by lazy {
        // Podemos meterle flow directamente!!!
        // ktorfit.responseConverter(FlowResponseConverter())
        Ktorfit.Builder()
            .httpClient {
                install(ContentNegotiation) {
                    json(Json { isLenient = true; ignoreUnknownKeys = true })
                }
                install(DefaultRequest) {
                    header(HttpHeaders.ContentType, ContentType.Application.Json)
                }
            }
            .baseUrl(API_URL)
            .build()
    }