这是我作为多部分设置文件和纯文本参数发送的请求。
suspend fun updateUserProfileInformation(
docList: List<File>,
type: String?,
reason: String?,
address: String?,
requiredOtp: String?
): CommonOtpRes {
val formData = formData {
for (file in docList) {
this.appendInput(
key = "documents",
headers = Headers.build {
append("Content-Type", "image/jpeg")
append(HttpHeaders.ContentLength, file.length())
append(HttpHeaders.ContentDisposition, "filename=${file.name}")
},
size = file.length()
) { buildPacket { writeFully(file.readBytes()) } }
}
append("type", type ?: "")
append("reason", reason ?: "")
if(!address.isNullOrEmpty())
append("address", address)
if(!requiredOtp.isNullOrEmpty())
append("requiredOTPS", requiredOtp)
}
val response: HttpResponse = client.post("$VERSION/user-request/submit") {
setBody(
MultiPartFormDataContent(
formData
)
)
}
return response.body()
}
现在我需要做的是,一旦被调用并收到响应,我需要检查是否收到某些特定的状态代码,然后我需要修改请求并使用更新的参数重新发送它,克隆以前的请求正文并添加其他类型参数给它。我收到 request.body 类型作为 StreamRequestBody 类型,现在这个类是内部的并且不能直接访问,因此需要找到一种方法来修改内容并使用新的附加参数重新发送它。
到目前为止我所尝试的,原始是收到响应后来自拦截器内部的请求。
if(original.body is RequestBody && original.body?.isOneShot() == true) {
val buffer = Buffer()
original.body?.writeTo(buffer)
val bodyString = buffer.readUtf8()
Timber.e("Body Contents: $bodyString")
}
但是这里收到的内容是空的。
以前使用Retrofit,这就是我实现相同目标的方法,但使用ktor 这是不可能的。
val builder: MultipartBody.Builder = MultipartBody.Builder()
.setType(MultipartBody.FORM)
val requestBody = original.body as MultipartBody
for (part in requestBody.parts) {
if (part.body.contentType() == "application/json; charset=utf-8".toMediaTypeOrNull() && part.headers?.get(
"Content-Disposition"
)?.contains("type") == true
) {
builder.addFormDataPart("type", Gson().toJson(list))
} else {
builder.addPart(part)
}
}
val multipartReq = original.newBuilder()
.method(original.method, builder.build())
.build()
return multipartReq
这在 Ktor 中比较困难,因为必须在
multipart/form-data
钩子中解析 Send
内容。您可以使用 CIOMultipartDataBase
内部类解析请求正文,然后将接收到的部分附加到 FormPart
列表中,该列表可用于为 MultiPartFormDataContent
类创建表单数据。可以使用 HttpRequestBuilder.takeFrom
方法复制原始请求。这是一个例子:
val client = HttpClient(CIO) {
install(createClientPlugin("test") {
on(Send) { request ->
val call = proceed(request)
val multipart = call.request.content
if (multipart !is MultiPartFormDataContent) return@on call
val readChannel = GlobalScope.writer {
multipart.writeTo(channel)
}.channel
val multipartParser = CIOMultipartDataBase(EmptyCoroutineContext, readChannel, multipart.contentType.toString(), multipart.contentLength)
// Copy parts
val newParts = mutableListOf<FormPart<*>>()
while (true) {
val part = multipartParser.readPart() ?: break
if (part.contentType == ContentType.Application.Json && part.contentDisposition?.name == "type") {
newParts.add(FormPart("type", "{}"))
} else {
when (part) {
is PartData.FormItem -> {
val name = part.name
if (name != null) {
newParts.add(FormPart(name, part.value, part.headers))
}
}
is PartData.FileItem -> {
val name = part.name
if (name != null) {
newParts.add(FormPart(name, InputProvider(block = part.provider), part.headers))
}
}
else -> error("")
}
}
}
val newRequest = HttpRequestBuilder().takeFrom(request) // Create a copy of the request
newRequest.setBody(
MultiPartFormDataContent(
parts = formData(*newParts.toTypedArray()),
boundary = multipart.boundary
)
)
proceed(newRequest)
}
})
}
val response = client.post("https://httpbin.org/post") {
setBody(
MultiPartFormDataContent(
formData {
append("description", "Ktor logo")
append("type", "123", Headers.build {
append(HttpHeaders.ContentType, "application/json")
})
append("image", File("archive.zip").readBytes(), Headers.build {
append(HttpHeaders.ContentType, "image/png")
append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
})
},
boundary = "WebAppBoundary"
)
)
}