Search code examples
androidretrofitokhttp

Android retrofit: 400 bad request response code


So I'm still in the process of learning android dev and I'm currently working on an app which is supposed to show students their grades. Right now I am stuck at getting login to a service from which grades are collected. For that process I am using https://eduo-ocjene-docs.vercel.app/ api (documentation is in Croatian). This is what curl request for logging in looks like:

curl --location --request GET 'https://ocjene.eduo.help/api/login' \--header 'Content-Type: application/json' \--data-raw '{    "username":"[email protected]", "password":"ivanovPassword123"}'

Here are screenshots of what I have tried until now

Here is how I build retrofit

object ApiModule {

    private const val BASE_URL = "https://ocjene.eduo.help/"

    lateinit var retrofit: EdnevnikApiService

    private val json = Json { ignoreUnknownKeys = true }

    fun initRetrofit() {
        val okhttp = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }).build()

        retrofit = Retrofit.Builder().baseUrl(BASE_URL)
            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
            .client(okhttp).build().create(EdnevnikApiService::class.java)
    }

}

The login method

interface EdnevnikApiService {

    @HTTP(method = "get", path = "/api/login", hasBody = true)
    fun login(@Body request: LoginRequest): Call<LoginResponse>

}

This is what happens when the login button is clicked

fun onLoginButtonClicked(email: String, password: String) {
    val request = LoginRequest(email, password)
    ApiModule.retrofit.login(request).enqueue(object : Callback<LoginResponse> {
        override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
            loginResultLiveData.value = response.isSuccessful
            val body = response.body()
        }

        override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
            loginResultLiveData.value = false
        }

    })
}

and this is what kotlin request and kotlin response data classes look like

@kotlinx.serialization.Serializable
data class LoginRequest(
    @SerialName("username") val username: String,
    @SerialName("password") val password: String,
)
@kotlinx.serialization.Serializable
data class LoginResponse(
    @SerialName("LoginSuccessful") val isSuccessful: Boolean,
)

Oh and this is what I get from the interceptor when I send the request enter image description here


Solution

  • My guess is server is responding with 400 Bad Request due to unsupported method type. When I replaced method = "get" with method = "GET" in your sample code, I received:

    java.lang.IllegalArgumentException: method GET must not have a request body.
    

    which makes sense. Luckily, the /login API you shared works with POST method type, so you can try using:

    @HTTP(method = "POST", path = "/api/login", hasBody = true,)
    

    I checked at my end and I received the following response:

    <-- 200 https://ocjene.eduo.help/api/login (1390ms)
    access-control-allow-origin: *
    access-control-allow-credentials: true
    set-cookie: epicCookie=f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a;
    content-type: application/json; charset=utf-8
    etag: "bkrbkvg0eo6c"
    vary: Accept-Encoding
    date: Thu, 10 Nov 2022 03:07:08 GMT
    server: Fly/b1863e2e7 (2022-11-09)
    via: 2 fly.io
    fly-request-id: 01GHFR2T56X9K0GFN3DH1Z9JYV-sin
    {"LoginSuccessful":false,"token":"f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a"}
    <-- END HTTP (228-byte body)