Как с помощью Retrofit и OkHttp решить задачу добавления header
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Реализация добавления заголовков через Retrofit и OkHttp
В экосистеме Retrofit и OkHttp существует несколько гибких способов добавления заголовков к HTTP-запросам. Выбор конкретного подхода зависит от того, нужны ли заголовки для всех запросов или только для конкретных эндпоинтов.
Основные подходы к добавлению заголовков
1. Интерсепторы OkHttp (для глобальных заголовков)
Наиболее мощный и рекомендуемый подход для добавления заголовков, которые должны присутствовать во всех запросах (например, токены аутентификации, версия API).
class AuthInterceptor(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val requestWithHeaders = originalRequest.newBuilder()
.header("Authorization", "Bearer $token")
.header("Content-Type", "application/json")
.header("User-Agent", "MyApp/1.0")
.build()
return chain.proceed(requestWithHeaders)
}
}
// Создание клиента OkHttp с интерсептором
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor("your_token_here"))
.build()
// Использование с Retrofit
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
Преимущества:
- Централизованное управление заголовками
- Автоматическое применение ко всем запросам
- Возможность динамического изменения заголовков
2. Аннотации Retrofit (для конкретных эндпоинтов)
Идеально подходит для заголовков, специфичных для отдельных API-методов.
interface ApiService {
// Статический заголовок
@GET("users/profile")
@Headers("Cache-Control: no-cache")
suspend fun getUserProfile(): UserProfile
// Динамический заголовок через аннотацию @Header
@GET("users/{id}")
suspend fun getUserById(
@Path("id") userId: String,
@Header("X-Client-Version") clientVersion: String
): User
// Несколько заголовков
@POST("orders")
@Headers(
"Content-Type: application/json",
"Accept: application/vnd.api+json"
)
suspend fun createOrder(@Body order: Order): OrderResponse
}
3. Кастомный Interceptor с условиями
Более продвинутый подход, позволяющий добавлять заголовки на основе условий.
class ConditionalInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val url = originalRequest.url.toString()
val requestBuilder = originalRequest.newBuilder()
// Добавляем заголовок только для определенных эндпоинтов
if (url.contains("/secure/")) {
requestBuilder.header("X-Secure-Request", "true")
}
// Добавляем временную метку для всех POST-запросов
if (originalRequest.method == "POST") {
requestBuilder.header("X-Request-Timestamp", System.currentTimeMillis().toString())
}
return chain.proceed(requestBuilder.build())
}
}
4. Аутентификация через Authenticator
Для автоматического обновления токенов аутентификации:
class TokenAuthenticator : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
return if (responseCount(response) >= 3) {
null // Слишком много попыток
} else {
val newToken = refreshToken() // Ваша логика обновления токена
response.request.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
}
}
private fun responseCount(response: Response): Int {
var count = 1
var r = response
while (r.priorResponse != null) {
count++
r = r.priorResponse!!
}
return count
}
}
Рекомендации по выбору подхода
- Глобальные заголовки (авторизация, версия приложения) → Используйте Interceptor
- Специфичные для эндпоинта заголовки → Используйте аннотации @Header или @Headers
- Динамические заголовки (зависящие от состояния приложения) → Комбинируйте Interceptor с передачей параметров
- Автоматическое обновление токенов → Используйте Authenticator
Пример комплексной реализации
class HeaderManager(private val context: Context) {
fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(LoggingInterceptor()) // Логирование
.addInterceptor(AuthInterceptor()) // Авторизация
.addInterceptor(NetworkInterceptor()) // Проверка сети
.authenticator(TokenAuthenticator()) // Обновление токенов
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
}
}
class AuthInterceptor @Inject constructor(
private val tokenManager: TokenManager
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val newRequest = request.newBuilder().apply {
tokenManager.getAccessToken()?.let { token ->
header("Authorization", "Bearer $token")
}
header("X-App-Version", BuildConfig.VERSION_NAME)
header("X-Platform", "Android")
}.build()
return chain.proceed(newRequest)
}
}
Ключевые моменты:
- Интерсепторы выполняются в порядке их добавления
- Используйте
addInterceptor()для запросов и ответов - Используйте
addNetworkInterceptor()только для сетевых операций - Для модификации заголовков в запросах используйте обычные интерсепторы
- Для обработки 401 ошибок и обновления токенов используйте Authenticator
Такой многоуровневый подход обеспечивает чистую архитектуру, легкую тестируемость и гибкость в управлении HTTP-заголовками в Android-приложении.