Конфигурация HTTP-клиента

Mithril.js предоставляет встроенный модуль m.request, который служит для выполнения асинхронных HTTP-запросов. Он сочетает в себе простоту использования и гибкость настройки, позволяя работать с REST API, GraphQL и любыми другими HTTP-сервисами.

Основы m.request

Функция m.request принимает объект конфигурации, где задаются следующие ключевые параметры:

  • method — HTTP-метод запроса (GET, POST, PUT, DELETE и др.). По умолчанию используется GET.
  • url — адрес ресурса, к которому выполняется запрос.
  • body — данные запроса для методов, которые поддерживают тело запроса (POST, PUT, PATCH).
  • params — объект с параметрами URL-запроса, автоматически сериализуемыми в строку запроса.
  • headers — объект с HTTP-заголовками, например { "Authorization": "Bearer TOKEN" }.
  • responseType — тип ожидаемого ответа (json, text, document и др.). По умолчанию json.
  • withCredentials — логическое значение для включения передачи куки.
  • timeout — максимальное время ожидания ответа в миллисекундах.
  • deserialize — функция для кастомной обработки данных ответа.
  • serialize — функция для кастомной сериализации тела запроса.

Пример базового GET-запроса с параметрами:

m.request({
    method: "GET",
    url: "/api/users",
    params: { page: 2, limit: 20 },
    headers: { "Authorization": "Bearer abc123" }
}).then(response => {
    console.log(response);
});

Настройка глобальных параметров

Для упрощения многократных запросов можно использовать глобальную конфигурацию через переопределение m.request с функцией-обёрткой. Это позволяет централизованно задавать заголовки, обработку ошибок и логику сериализации.

Пример глобальной конфигурации с обработкой ошибок:

function apiRequest(options) {
    return m.request({
        ...options,
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${localStorage.getItem('token')}`,
            ...(options.headers || {})
        },
        deserialize: data => JSON.parse(data),
    }).catch(err => {
        console.error("Ошибка API:", err);
        throw err;
    });
}

// Использование
apiRequest({ method: "GET", url: "/api/profile" })
    .then(profile => console.log(profile));

Асинхронная обработка и интеграция с компонентами

Mithril-компоненты могут напрямую использовать промисы, возвращаемые m.request, для динамического обновления интерфейса. Ключевой момент — вызов m.redraw() не обязателен, так как Mithril автоматически перерисовывает компоненты после разрешения промиса.

Пример интеграции запроса в компонент:

const UserList = {
    users: [],
    oninit() {
        m.request({ method: "GET", url: "/api/users" })
            .then(data => { UserList.users = data; });
    },
    view() {
        return m("ul",
            UserList.users.map(user => m("li", user.name))
        );
    }
};

Работа с кастомной сериализацией и десериализацией

Для сложных API часто требуется изменять формат отправляемых данных или обрабатывать нестандартные ответы. serialize и deserialize предоставляют полный контроль:

m.request({
    method: "POST",
    url: "/api/upload",
    body: { file: fileData },
    serialize: body => {
        const formData = new FormData();
        formData.append("file", body.file);
        return formData;
    },
    deserialize: response => response.status === "ok"
})
.then(result => console.log(result));

Отмена запросов

Mithril поддерживает отмену запросов через объект AbortController, что особенно важно для предотвращения гонок данных и утечек памяти при динамическом обновлении интерфейса:

const controller = new AbortController();

m.request({
    method: "GET",
    url: "/api/long-process",
    signal: controller.signal
}).catch(err => {
    if (err.name === "AbortError") console.log("Запрос отменён");
});

// Отмена запроса
controller.abort();

Настройка таймаута

Таймаут задаётся в миллисекундах через опцию timeout. Если сервер не отвечает в течение заданного времени, промис отклоняется:

m.request({
    method: "GET",
    url: "/api/data",
    timeout: 5000
}).catch(err => console.error("Таймаут запроса:", err));

Поддержка сессий и кук

Опция withCredentials позволяет отправлять куки вместе с запросом. Это важно для авторизации через сессионные токены на сервере:

m.request({
    method: "GET",
    url: "/api/private",
    withCredentials: true
});

Использование с REST и GraphQL

Для REST m.request удобно использовать с params и body. Для GraphQL часто применяется POST-запрос с JSON-телом:

m.request({
    method: "POST",
    url: "/graphql",
    body: { query: "{ users { id, name } }" },
    headers: { "Content-Type": "application/json" }
});

Обработка ошибок и статусов

m.request по умолчанию отклоняет промис при статусах HTTP 4xx и 5xx. Для кастомной обработки можно использовать then и catch, а также проверять поле status объекта ошибки:

m.request({ method: "GET", url: "/api/missing" })
    .then(data => console.log("Данные:", data))
    .catch(err => {
        console.log("Статус:", err.code);
        console.log("Сообщение:", err.message);
    });

Итоговые рекомендации по конфигурации

  • Использовать глобальные обёртки для повторяющихся настроек (заголовки, обработка ошибок).
  • Применять кастомные функции serialize/deserialize при работе с нестандартными API.
  • Всегда обрабатывать ошибки и таймауты, чтобы избегать зависаний интерфейса.
  • Для динамических компонентов применять асинхронные запросы напрямую в oninit или других жизненных циклах.
  • Использовать AbortController для отмены долгих или устаревших запросов.

Эта конфигурация обеспечивает гибкость и надёжность работы с любыми HTTP-сервисами, интегрируя их напрямую в реактивную архитектуру Mithril.