Search code examples
androidretrofitretrofit2okhttp

Sending a boolean parameter with other parameters (where one of them is file) fails


Server accepts all parameters except boolean one with Multipart form request data

So I have such request:

@Multipart
@POST("auth/...")
suspend fun sendExampleRequest(
    @Part("enabled") enabled: Boolean,
    @Part("name") name: String,
    @Part photo: MultipartBody.Part
)

Call it from:

override suspend fun sendExampleRequest(
    enabled: Boolean,
    name: String,
    photo : Uri
) {
    try {
        withContext(ioDispatcher) {
            authApi.sendExampleRequest(
                enabled = enabled,
                name = name,
                photo = document.toMultipartBodyPart(context, "photo")
            )
        }
    } catch (e: Throwable) {
        // ...
    }
}

But server responds with error only for boolean parameter that it has the wrong value.

REQUEST:

--> POST https://...
Content-Type: multipart/form-data; boundary=240f3a38-2e91-4b6c-acd0-c04ba8c0e81a
Content-Length: 68700
accept: application/json
Authorization: Bearer ...

--240f3a38-2e91-4b6c-acd0-c04ba8c0e81a
Content-Disposition: form-data; name="enabled"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=UTF-8
Content-Length: 4
true
--240f3a38-2e91-4b6c-acd0-c04ba8c0e81a
Content-Disposition: form-data; name="name"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=UTF-8
Content-Length: 4
test
--240f3a38-2e91-4b6c-acd0-c04ba8c0e81a
Content-Disposition: form-data; name="document"; filename="test_photo_file.jpg"
Content-Type: image/jpeg
Content-Length: 67794
...

RESPONSE:

<-- 422 https://... (328ms)
server: nginx
date: Thu, 30 Mar 2023 16:49:08 GMT
content-type: application/json
cache-control: no-cache, private
x-ratelimit-limit: 60
x-ratelimit-remaining: 58
access-control-allow-origin: *
{"message":"The enabled field must be true or false.","errors":{"enabled":["The enabled field must be true or false."]}}
<-- END HTTP (149-byte body)

I can just send JSON body instead but without file:

{
  "enabled": false,
  "name": "test"
}

then it doesn't give error for boolean parameter but of course it gives the error about document file, so I can use only Multipart form to send all needed parameters but there is some issue with boolean parameters

Retrofit setup:

@Provides
@Singleton
fun provideRetrofit(
    gson: Gson,
    client: OkHttpClient,
    queryConverterFactory: Converter.Factory
): Retrofit = Retrofit.Builder()
    .baseUrl(BuildConfig.BASE_URL)
    .client(client)
    .addConverterFactory(queryConverterFactory)
    .addConverterFactory(ScalarsConverterFactory.create()) // added it but didn't help
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

Solution

  • Solved. It's a PHP server issue:

    it accepts 1/0 only for form-data, a backend dev has to parse true/false as well