Что такое Interceptor (перехватчик) при работе с сетью в Flutter?

Ответ

Interceptor — это паттерн, позволяющий перехватывать и модифицировать HTTP-запросы и ответы на клиентской стороне. В Flutter он часто реализуется в HTTP-клиентах, таких как Dio или http, и действует как цепочка middleware.

Основные сценарии использования:

  1. Добавление общих заголовков: Автоматическое подставление токена авторизации.
  2. Логирование: Запись деталей запросов и ответов для отладки.
  3. Обработка ошибок: Глобальный перехват ошибок (например, 401 Unauthorized) для обновления токена и повторения запроса.
  4. Кеширование: Возврат закешированного ответа при определенных условиях.

Практический пример с Dio:

import 'package:dio/dio.dart';

final dio = Dio();

// Добавляем перехватчик для логирования и авторизации
dio.interceptors.add(
  InterceptorsWrapper(
    onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
      // Логируем запрос
      print('[${options.method}] ${options.uri}');
      // Добавляем Bearer Token из локального хранилища
      final token = await SecureStorage().getToken();
      if (token != null) {
        options.headers['Authorization'] = 'Bearer $token';
      }
      return handler.next(options); // Продолжаем выполнение
    },
    onResponse: (Response response, ResponseInterceptorHandler handler) {
      print('Response Status: ${response.statusCode}');
      return handler.next(response);
    },
    onError: (DioException error, ErrorInterceptorHandler handler) async {
      print('Error: ${error.message}');
      // Пример: повтор запроса при 401 ошибке
      if (error.response?.statusCode == 401) {
        try {
          // Пытаемся обновить токен
          final newToken = await AuthService().refreshToken();
          // Обновляем заголовок в исходном запросе
          error.requestOptions.headers['Authorization'] = 'Bearer $newToken';
          // Повторяем оригинальный запрос
          final response = await dio.fetch(error.requestOptions);
          return handler.resolve(response);
        } catch (e) {
          return handler.next(error); // Пробрасываем ошибку дальше
        }
      }
      return handler.next(error);
    },
  ),
);

Можно добавлять несколько интерцепторов, и они будут выполняться в порядке добавления.

Ответ 18+ 🔞

Слушай, а вот это Interceptor — вообще тема, ебать мои старые костыли! Это такая штука, которая как хитрая жопа, встраивается в твой HTTP-клиент и может всё перехватить по дороге: запрос, ответ, ошибку. Представь, будто ты ставишь своего чела на почту, который все письма проверяет, что-то дописывает или, если пришла хуйня, сразу решает проблему, пока ты чай пьёшь.

Где это, блядь, пригождается, спросишь ты?

  1. Заголовки автоматом. Ну, ёпта, надоело же в каждом запросе вручную токен авторизации пихать? Interceptor сам его из хранилища достанет и прилепит. Красота.
  2. Логирование. Хочешь видеть, что куда ушло и что вернулось? Он всё запишет, как на духу. Для отладки — просто пиздец как удобно.
  3. Обработка косяков. Вот прилетает ошибка 401 (токен протух). Ты бы сам начинал писать гору кода, а interceptor может сам попробовать токен обновить и запрос повторить. Волнение ебать — снимает сразу.
  4. Кеширование. Можно научить его, чтобы он, если данные уже спрашивали, не лез в сеть, а отдавал из кэша. Умная жесть.

Смотри, как это на Dio выглядит, реальный пример:

import 'package:dio/dio.dart';

final dio = Dio();

// Вешаем нашего перехватчика-надзирателя
dio.interceptors.add(
  InterceptorsWrapper(
    onRequest: (RequestOptions options, RequestInterceptorHandler handler) async {
      // Логируем, что за запрос пошёл
      print('[${options.method}] ${options.uri}');
      // Достаём токен (типа из безопасного хранилища)
      final token = await SecureStorage().getToken();
      if (token != null) {
        // И автоматом в заголовки его суём
        options.headers['Authorization'] = 'Bearer $token';
      }
      // Говорим: "окей, чувак, дальше иди"
      return handler.next(options);
    },
    onResponse: (Response response, ResponseInterceptorHandler handler) {
      // Логируем ответ, всё чики-пуки
      print('Response Status: ${response.statusCode}');
      return handler.next(response);
    },
    onError: (DioException error, ErrorInterceptorHandler handler) async {
      print('Error: ${error.message}');
      // А вот тут магия: если ошибка 401 (не авторизован)
      if (error.response?.statusCode == 401) {
        try {
          // Пытаемся по-тихому обновить токен
          final newToken = await AuthService().refreshToken();
          // Обновляем заголовок в том запросе, который упал
          error.requestOptions.headers['Authorization'] = 'Bearer $newToken';
          // Пробуем отправить этот же запрос ещё раз!
          final response = await dio.fetch(error.requestOptions);
          // И отдаём успешный результат как будто так и было
          return handler.resolve(response);
        } catch (e) {
          // Не вышло обновить — ну, извини, лети дальше, ошибка
          return handler.next(error);
        }
      }
      // Если это не 401, то тоже просто прокидываем ошибку дальше
      return handler.next(error);
    },
  ),
);

И таких перехватчиков можно навесить овердохуища — целую цепочку. Они будут работать один за другим, как на конвейере. Главное, код в блоках выше не трогай, там всё правильно. В общем, вещь мощная, жизнь упрощает конкретно.