'Can I change ContentType header for a particular request after declaring ContentType = Application.Json in DefaultRequest of my KtorClient

I'm working on my android project and I'm making a request function for uploading file that makes request as given below,

httpClient.put(uri) {
    body = MultiPartFormDataContent(formData {
        append("file", fileContent, Headers.build {
            append(HttpHeaders.ContentType, fileMimeType)
            append(HttpHeaders.ContentDisposition, ContentDisposition.File.withParameter(ContentDisposition.Parameters.FileName, fileOriginalName))
        })
    })
}

So as you can see this request has a ContentType header, but I already declared ContentType header in my Ktor HttpClient,

install(DefaultRequest) {
                url {
                    protocol = URLProtocol.HTTP
                    host = baseURL
                }

                headers {
                    append(HttpHeaders.ContentType, ContentType.Application.Json)
                    append(HttpHeaders.Authorization, accessToken)
                    append(USER, user)
                }
            }

So my question is which ContentType is my request going to take? if it can't take the ContentType that I specified in my request function then how can I make it accept ContentType different than default one?



Solution 1:[1]

If you try to set the Content-Type header in the DefaultRequest feature then you will get io.ktor.http.UnsafeHeaderException: Header(s) [Content-Type] are controlled by the engine and cannot be set explicitly.

So if you remove the line append(HttpHeaders.ContentType,ContentType.Application.Json) then the Content-Type of a request will be multipart/form-data and the Content-Type for the file body part ? value of fileMimeType variable.

Solution 2:[2]

So I was fiddling around the same issue found out from some examples that

  1. If you are using Multipart, you need to add Content in body
  2. Header shouldnot be added at all in the request, as this will now automatically be handled by Ktor engine. Since, you already added header in the body.

Here is an example

return helper.getAuthClient(isMultiPartRequest = true).put {
        url {
            takeFrom(ApiConstants.BASE_URL)
            encodedPath = ApiConstants.UPLOAD_DOCUMENT
            body = MultiPartFormDataContent (
                formData {
                    append(
                        key = "file",
                        value =  request.file,
                        headers = io.ktor.http.Headers.build {
                            append(HttpHeaders.ContentDisposition, "filename=${request.fileName}")
                        }
                    )
                    append("fileName", request.fileName)
                }
            )

        }
    }

Here is what I do for headers.

private fun addCommonHeaders(headers: HeadersBuilder, isMultiPartRequest: Boolean = false) {
        headers.append(Headers.API_KEY, ApiConstants.API_KEY)
        headers.append(Headers.CORR_ID, randomUUID())
        headers.append(Headers.API_VERSION, Headers.Values.VERSION_V3)
        headers.append(Headers.PLATFORM, getUserPlatform())

        if(!isMultiPartRequest) {
            headers.append(Headers.CONTENT_TYPE, Headers.Values.CONTENT_JSON)
        }// this is where I decide, for Multipart, don't add any Content-Type headers.
    }

points to note:

  1. key = the request key where you will send the byteArray
  2. add more parameters, if your API contract demands, by further adding append() in succession

Note : getAuthClient() is simple custom function that provides the instance of HttpClient so ignore that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aleksei Tirman
Solution 2 sud007