В Kotlin Android KTor POST-запрос с данными из нескольких частей возвращает неверный запрос FormData

Я пытаюсь получить доступ к Stability AI в Android с помощью клиента Ktor, код выглядит так

suspend fun generateImageOfText(text: String,aspectRatio: String = "1:1", apiKey: String): Result {
    return try {
        val response = httpClient.post("https://api.stability.ai/v2beta/stable-image/generate/ultra") {
            header(HttpHeaders.Authorization, "Bearer $apiKey")
            header(HttpHeaders.Accept, "application/json")
            setBody(MultiPartFormDataContent(
                formData {
                    append("prompt", text)
                },
                boundary = "WebAppBoundary"
            ))
        }
        val resultText = response.bodyAsText()
        Log.d("APIConnector", "generateImageOfText: status ${response.status} result $resultText")
        Result.Success(resultText)
    }catch (e: Exception){
        e.printStackTrace()
        (Result.Error(e.message ?: "Unknown error"))
    }
}

что приводит к

400 Неверный ответ на запрос {"успех":false,"message":"неверный формат Запрос данных формы. Заголовок Content-Disposition в части FormData пропало имя."}

API стабильности требует, чтобы мы предоставили ему данные из нескольких частей формы, но куда мне следует включить этот заголовок размещения содержимого? Я проверил официальную документацию, где приведен пример загрузки файла изображения, который выглядит следующим образом.

val client = HttpClient(CIO)

val response: HttpResponse = client.post("http://localhost:8080/upload") {
    setBody(MultiPartFormDataContent(
        formData {
            append("description", "Ktor logo")
            append("image", File("ktor_logo.png").readBytes(), Headers.build {
                append(HttpHeaders.ContentType, "image/png")
                append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
            })
        },
        boundary = "WebAppBoundary"
    )
    )
    onUpload { bytesSentTotal, contentLength ->
        println("Sent $bytesSentTotal bytes from $contentLength")
    }
}

в этом примере файл загружается, и его имя файла отправляется с использованием заголовка Content-Disposition, но в моем случае у меня нет файла для загрузки, но я пытался передать строку в качестве имени, но это дало ту же ошибку, API работает в почтальоне с теми же параметрами

РЕДАКТИРОВАТЬ 1: я пытался включить тип контента в блок formData, но это было бесполезно.

formData {
     append("prompt", text, Headers.build {
         append(HttpHeaders.ContentType, "text/plain")
     }) }

Редактировать 2: После добавления дополнительных кавычек, как было предложено в ответе и комментариях, я смог двигаться дальше, большое спасибо, окончательный код выглядит так

suspend fun generateImageOfText(text: String,aspectRatio: String = "1:1", apiKey: String): Result {
        return try {
            val response = httpClient.post("https://api.stability.ai/v2beta/stable-image/generate/core") {
                header(HttpHeaders.Authorization, "Bearer $apiKey")
                header(HttpHeaders.Accept, "application/json")
                setBody(MultiPartFormDataContent(
                    formData {
                        append("\"prompt\"", text, Headers.build {
                            append(HttpHeaders.ContentDisposition, "form-data; name=\"prompt\"")
                        })
                    }
                ))
            }
            val result: Base64Result = response.body()
            Result.Success(result)
        }catch (e: Exception){
            e.printStackTrace()
            (Result.Error(e.message ?: "Unknown error"))
        }
    }

Попробуйте использовать Fiddler или Wireshak, чтобы зафиксировать неправильный и правильный запрос. Сравните их. В моем случае точно такая же ошибка была вызвана отсутствием кавычек в значении имени Content-Disposition: Content-Disposition: form-data; name=prompt vs Content-Disposition: form-data; name = "prompt"

jasniec 15.06.2024 00:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня была такая же проблема в C#, и я также получил сообщение об ошибке Malformed FormData request. Content-Disposition header in FormData part is missing a name. По какой-то странной причине мне пришлось заключить имена параметров в две кавычки.

var prompt = "Cat on the moon";
var format = "3:2";

using var content = new MultipartFormDataContent()
{
    { new StringContent(prompt), "\"prompt\"" },
    { new StringContent("png"), "\"output_format\"" },
    { new StringContent(format), "\"aspect_ratio\"" }
};

var url = "https://api.stability.ai/v2beta/stable-image/generate/ultra";
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
    Content = content
};

request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("image/*"));
var response = await _httpClient.SendAsync(request);

большое спасибо за ответ, мне помогло добавление кавычек в подсказку

trinadh thatakula 20.06.2024 15:14

Другие вопросы по теме