我无法两次接收 ktor HttpClient 的 json 正文。 对于服务器,有一个 DoubleReceive 功能,但我不知道在进行客户端调用时如何使用它。
我想调用一个不同的微服务,它要么返回一些 json,要么当出现错误时返回,例如状态 500 和错误描述 json 负载。
所以我尝试了 HttpResponseValidator ,并且只允许使用此代码进行 readBytes
HttpResponseValidator {
validateResponse { response ->
val statusCode = response.status.value
val originCall = response.call
if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) return@validateResponse
response.use {
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
//try parse error from json payload which other microservice usually send
exceptionCall.response.receiveError()?.also { throw mapErrors(response, it) }
//default ktor exception mapping
when (statusCode) {
in 300..399 -> throw RedirectResponseException(response)
in 400..499 -> throw ClientRequestException(response)
in 500..599 -> throw ServerResponseException(response)
}
if (statusCode >= 600) {
throw ResponseException(response)
}
}
}
}
receiveError 可以用作
JacksonConfig.defaultMapper.readValue<ServiceErrorResponse>(this.readBytes())
但如果你只是调用 response.receive<ServiceErrorResponse>()
则会抛出 DoubleReceivException
原因是接收函数首先检查 received atomicBoolean
。
TL;博士 现在我想知道是否有关于如何处理错误有效负载的任何想法,或者您只是不使用它们?我对这种方式的微服务很陌生,因此需要添加它们。 Ktor 是一个新成员。如何在服务之间传递错误信息?
还有一种方法可以在客户端使用 DoubleReceive 功能。因为
HttpClient(){install(DoubleReceive)}
不起作用,因为它不是 ApplicationFeature 也不是 ClientFeature。
Ktor 开发了一个名为 Double Receive 的实验性插件,您可以使用它来接收任意数量的请求体。
尝试制作自定义插件。看来这个解决方案可以提供帮助。至少对我有用。
自定义重试插件.kt
internal class CustomRetryPluginConfig {
var retries = 2
internal var modifyRequest: suspend (builder: HttpRequestBuilder) -> Unit = {}
internal var retryIf: suspend (httpResponse: HttpResponse) -> Boolean = { false }
fun shouldRetry(block: suspend (httpResponse: HttpResponse) -> Boolean) {
retryIf = block
}
fun modifyRequest(block: suspend (builder: HttpRequestBuilder) -> Unit) {
modifyRequest = block
}
}
internal val CustomRetryPlugin = createClientPlugin("RetryPlugin", ::CustomRetryPluginConfig) {
val retries = pluginConfig.retries
val modifyRequest = pluginConfig.modifyRequest
val shouldRetry = pluginConfig.retryIf
on(Send) { request ->
var originalCall = proceed(request)
var tryCount = retries
while (tryCount > 0) {
originalCall.response.run {
val savedCall = originalCall.save()
val response = savedCall.response
if (shouldRetry(response)) {
modifyRequest(request)
tryCount -= 1
originalCall = proceed(request)
} else {
tryCount = 0
originalCall = savedCall
}
}
}
originalCall
}
}
用途:
install(CustomRetryPlugin) {
shouldRetry { response ->
// make any checks for error inside body
}
modifyRequest { builder ->
// modify any new headers or params
}
}