Как перехватить и отредактировать тело http-запроса с помощью ktor-client (KMM)Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Как перехватить и отредактировать тело http-запроса с помощью ktor-client (KMM)

Сообщение Anonymous »

Моя задача — добавить к каждому HTTP-запросу, который отправляется через клиент ktor, определенные параметры в тело.
Когда я использовал модификацию, я легко делал это с помощью перехватчиков.
>В связи с переходом на КММ нам необходимо переоборудовать эти перехватчики в кТор. В настоящее время я использую okhttp3 в качестве движка, но хочу переключиться на CIO из-за его многоплатформенных возможностей. Не знаю, будет ли этот переключатель иметь большое значение для перехватчиков.
Я пробовал, но до сих пор ничего не получалось, и сейчас я немного беспомощен, как решить эту проблему. p>
Вот мой подход, который не работал.
Я знаю, что у kTor также есть перехватчики, такие как модернизация.
Поэтому я создал собственный перехватчик, который включил мой перехватчик сетевого менеджера. Затем я обновил тело перехватчика сетевого менеджера в контексте. Разве это не должно переопределять тело первоначального http-запроса?
NetworkModule:
private fun provideKtorClient(
config: AppConfig,
networkParamsInterceptor: NetworkParamsInterceptor,
json: Json,
): HttpClient {
return HttpClient(OkHttp) {
expectSuccess = false
install(Logging) {
logger = Logger.DEFAULT
level = if (BuildConfig.DEBUG) LogLevel.ALL else LogLevel.NONE
}
install(ContentNegotiation) {
json(json)
}
defaultRequest {
url(config.baseUrl)
contentType(ContentType.Application.Json)
}
engine {}

install(InterceptorImpl) {
interceptor = networkParamsInterceptor
}
}
}

private fun provideJson(): Json {
return Json {
ignoreUnknownKeys = true
isLenient = true
coerceInputValues = true
}
}

InterceptorImpl: (Здесь я также пытался добавить новое тело к методу continueWith внутри метода установки, но это также вызвало у меня ошибки, связанные с невозможностью корректной сериализации содержимого)
interface HttpClientInterceptor {
fun intercept(context: HttpRequestBuilder): HttpRequestBuilder
}

class InterceptorImpl(private var config: Config) {
class Config {
lateinit var interceptor: HttpClientInterceptor
}

companion object : HttpClientPlugin {
override val key: AttributeKey = AttributeKey("CustomInterceptors")

override fun prepare(block: Config.() -> Unit): InterceptorImpl {
val config = Config().apply(block)
return InterceptorImpl(config)
}

override fun install(plugin: InterceptorImpl, scope: HttpClient) {
scope.requestPipeline.intercept(HttpRequestPipeline.State) {
plugin.config.interceptor.intercept(context)
}
}
}

}

NetworkParamsInterceptor: (Здесь данные добавляются в новое тело и почему-то это ничего не делает. При регистрации данных тело добавляется правильно со всеми необходимыми данными, но при окончательной отправке запрос, затем тело снова сбрасывается только на тот параметр, который был отправлен из первоначального запроса --> будет показан после этого файла)
class NetworkParamsInterceptor(
private val context: Context,
private val userPreferences: UserPreferences,
) : HttpClientInterceptor {

private val defaultParams: Map by lazy { buildDefaultParams() }

@OptIn(InternalAPI::class)
override fun intercept(context: HttpRequestBuilder): HttpRequestBuilder {
return context.apply {
// Set default headers
defaultParams.forEach { (key, value) -> header(key, value) }

// For POST requests, add default parameters to the body
if (method.value.equals("POST", ignoreCase = true)) {
val contentType = headers["Content-Type"]
if(body is FormDataContent){
val bod = body as FormDataContent
bod.formData.forEach { s, strings -> println("XXX 2: $s, $strings")}
val newBody = mergeParamsWithJsonBody(bod, defaultParams)
body = newBody
bodyType = typeInfo()
}
}
}
}

private fun buildDefaultParams(): Map {
val map = mutableMapOf(
"os_version" to android.os.Build.VERSION.SDK_INT.toString(),
"device_model" to android.os.Build.MODEL,
"hl" to java.util.Locale.getDefault().toString()
)

val apiKey = context.packageManager.getApplicationInfo(
context.packageName,
PackageManager.GET_META_DATA
).metaData?.getString("findpenguins.app-api.key")
apiKey?.let { map["key"] = it }

val appVersion = context.packageManager.getPackageInfo(context.packageName, 0).versionName
appVersion?.let { map["app_version"] = it }

userPreferences.getAccessToken()?.let { map["access_token"] = it }
return map
}

private fun mergeParamsWithJsonBody(oldBody: FormDataContent, params: Map): JsonObject {
return buildJsonObject {
// Copy existing parameters
oldBody.formData.forEach { s, strings ->
put(s, JsonPrimitive(strings.first()))
}
// Add new parameters
params.forEach { (key, value) ->
put(key, JsonPrimitive(value))
}
}
}

TripCalendarApiService (начальный запрос, в котором тело добавляется правильно)
class TripCalendarApiService(private val client: HttpClient, private val json: Json) {

suspend fun getCalendarTrip(tripId: String): Result {
return try {
val response = client.post {
url {
path("trip", "calendar")
contentType(ContentType.Application.FormUrlEncoded)
setBody(FormDataContent(Parameters.build {
append("trip", tripId)
}))
}
}

val responseBody = response.bodyAsText()

if (response.status == HttpStatusCode.OK) {
val tripResponse = json.decodeFromString(responseBody)
Result.success(tripResponse)
} else {
Result.failure(DataError.ApiError(response.status.value, Throwable(responseBody)))
}
} catch (e: Exception) {
Result.failure(DataError.UnknownError(e))
}
}
}


Подробнее здесь: https://stackoverflow.com/questions/784 ... client-kmm
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»