Как добавить общий заголовок с токеном авторизации ко всем запросам в Retrofit и OkHttp
Что проверяют
Знаете ли вы, где в OkHttp место для сквозной логики, и понимаете ли разницу между добавлением токена и его обновлением. Почти всегда следом спрашивают, что делать, когда токен истёк и сервер вернул 401.
Как отвечать
Базовый ответ: Interceptor, подключённый к OkHttpClient через addInterceptor, а клиент передаётся в Retrofit.Builder().client(...). Опыт видно по деталям:
- токен не в конструкторе: интерсептор получает не строку, а источник вроде
TokenStorageи читает актуальное значение на каждом запросе. Иначе после обновления клиент продолжит слать старый токен; headerвместоaddHeader: первый заменяет значение, второй добавляет ещё один заголовок, и можно получить дваAuthorization;- обновление по 401: для этого в OkHttp есть
Authenticator, подключаемый через.authenticator(...). Он вызывается на ответ 401, обновляет токен и возвращает повторный запрос илиnull, чтобы сдаться. Обновление синхронизируют, чтобы пять параллельных запросов не запустили пять refresh, а число попыток ограничивают по цепочкеresponse.priorResponse; - исключения: логин и refresh токен получать не должны. Проверяют путь запроса или читают аннотации метода из тега
Invocation, который Retrofit кладёт в запрос; - application против network:
addInterceptorвызывается один раз на вызов даже при ответе из кеша,addNetworkInterceptorвидит реальные сетевые запросы и редиректы. Для токена хватает первого.
С Hilt клиент и Retrofit собирают в модуле со скоупом @Singleton, чтобы не плодить пулы соединений.
Где спотыкаются
Оставляют HttpLoggingInterceptor на уровне BODY в релизе, и токен попадает в логи. Есть redactHeader("Authorization"), но в релизе логирование лучше выключать совсем.
Обновляют токен прямо в интерсепторе на каждый 401 без синхронизации. Параллельные запросы соревнуются, и одноразовый refresh-токен сгорает после первого обновления, а остальные получают отказ.
Добавляют заголовок через @Header в каждый метод интерфейса. Это работает, но токен приходится протаскивать через все вызовы, и в новом методе его легко забыть.