Search code examples
kotlinktorktor-client

Sending a form urlencoded POST request with Ktor Client?


I am wanting to utilize the Ktor client library to interact with a Web API, specifically to authenticate via OAuth, and I'm running into trouble attempting to send a form urlencoded POST request properly. It would appear the actual body is not being sent for some reason.

I've tried looking at similar questions on SO, such as this question but I believe Ktor's API must have changed. I've also taken a look at JetBrains' documentation on this exact type of request here.

I am using the following to send my request:


val response = client.submitForm() {
            url("http://localhost:5555/oauth/token/")
            parameter("code", authData.authCode)
            parameter("grant_type", "authorization_code")
            parameter("client_id", clientId)
            parameter("client_secret", clientSecret)

            header("X-API-Key", getApiKey())
            method = HttpMethod.Post

        }

Which results in the following request being logged from a quick Express.js application I built to mock the request:

----- Body -----
{}
----- Headers -----
{
  host: 'localhost:5555',
  'content-length': '0',
  'x-api-key': 'api_key_here',
  'accept-charset': 'UTF-8',
  accept: '*/*',
  'user-agent': 'Ktor client',
  'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
}

My header is being sent just fine, however the body is empty.

I've tried a few other variations as well, such as:

        val response = client.post("http://localhost:5555/oauth/token/") {
            formData {
                parameter("test", "test")
            }

        }

Which results in:

----- Body -----
{}
----- Headers -----
{
  host: 'localhost:5555',
  'content-length': '0',
  'accept-charset': 'UTF-8',
  accept: '*/*',
  'user-agent': 'Ktor client'
}

as well as:

        val response = client.post("http://localhost:5555/oauth/token/") {
            FormDataContent(Parameters.build {
                append("code", authData.authCode!!)
                append("grant_type", "authorization_code")
                append("client_id", clientId)
                append("client_secret", clientSecret)
            })

            header("X-API-Key", getApiKey())
        }

Which shapes up similar to the first set of logs:

----- Body -----
{}
----- Headers -----
{
  host: 'localhost:5555',
  'content-length': '0',
  'x-api-key': 'api_key_here',
  'accept-charset': 'UTF-8',
  accept: '*/*',
  'user-agent': 'Ktor client'
}

For additional context, I am using the following libraries / versions:

  • io.ktor:ktor-client-core:2.0.2
  • io.ktor:ktor-client-cio:2.0.2
  • Kotlin: 1.7.0 (have also tried 1.6.20)
  • Built with Java 18, targeting Java 8 (which also has not had any effect upon changing the build target)

I'm sure it's something simple, but I've had no luck thus far with any of my permutations that I've tried.


Solution

  • You can set an instance of FormDataContent to a request body to send application/x-www-form-urlencoded request. To create an object of FormDataContent class you need to pass an instance of Parameters to its constructor.

    val response: HttpResponse = client.post("http://localhost:5555/oauth/token/") {
        header("X-API-Key", "api key")
        setBody(
            FormDataContent(
                Parameters.build {
                    append("code", "code")
                    append("grant_type", "authorization_code")
                    append("client_id", "client id")
                    append("client_secret", "client secret")
                }
            )
        )
    }