С ростом количества современных веб-приложений обеспечение модульности, гибкости и удобства обслуживания стало критически важным. Один из способов добиться этого — эффективно использовать шаблоны проектирования. Сегодня мы создадим гибкий HttpService для приложений React, используя Facade Pattern.

Что такое узор фасада?

Фасадный шаблон обеспечивает упрощенный интерфейс для более сложной подсистемы. Вместо того, чтобы делать HTTP-запросы с разными заголовками и конфигурациями во всем приложении, мы создадим централизованную службу (HttpService), которая скроет всю сложность. Этот:

  • Сокращает количество ошибок за счет централизации логики.
  • Повышает читаемость кода и удобство сопровождения.
  • Обеспечивает гибкость для изменения базовой библиотеки (например, axios), не затрагивая остальную часть приложения.

Шаг 1: Настройка

Прежде чем мы начнем, убедитесь, что у вас установлен axios:

npm install axios

Шаг 2: Базовая структура

Давайте начнем с создания базовой структуры для нашего сервиса. Это позволит управлять нашим базовым URL-адресом по умолчанию и создать экземпляр axios:

import axios from 'axios';

class HttpService {
    constructor(baseURL = 'https://api.yourdomain.com') {
        this.baseUrl = baseURL;
        this.instance = axios.create({ baseURL: this.baseUrl });
    }
}

Шаг 3: Обработка заголовков

Для эффективной обработки токенов аутентификации и других заголовков:

get defaultHeaders() {
    return {
        'Authorization': localStorage.getItem('Authorization'),
        'Content-Type': 'application/json',
    };
}

Шаг 4: Основной метод запроса

Вместо того, чтобы повторять логику для get, post и т. д., у нас будет основной метод request:

async request(method, url, data = null, customHeaders = {}) {
    const headers = { ...this.defaultHeaders, ...customHeaders };
    const source = axios.CancelToken.source();

    const config = {
        method,
        url,
        headers,
        cancelToken: source.token
    };

    if (data) {
        config.data = data;
    }

    return {
        request: this.instance(config),
        cancel: source.cancel
    };
}

Шаг 5: Определение конкретных методов HTTP

Используя основной метод request, мы определим конкретные методы HTTP:

get(url, customHeaders = {}) {
    return this.request('get', url, null, customHeaders);
}

post(url, data, customHeaders = {}) {
    return this.request('post', url, data, customHeaders);
}

put(url, data, customHeaders = {}) {
    return this.request('put', url, data, customHeaders);
}

delete(url, customHeaders = {}) {
    return this.request('delete', url, null, customHeaders);
}

Шаг 6: Понимание request и cancel

Хотя метод request довольно прост — он отправляет HTTP-запрос и возвращает обещание, функциональность cancel заслуживает особого внимания.

Зачем отменять запрос?

В современных веб-приложениях, особенно с широким взаимодействием с пользователем, могут быть сценарии, в которых текущий HTTP-запрос становится избыточным. Например, пользователь может уйти от текущего представления или инициировать новый запрос, который делает предыдущий ненужным. В этих случаях возможность прервать предыдущий запрос может сэкономить ценные сетевые ресурсы и предотвратить непредвиденные побочные эффекты.

Введите: прервать контроллер

Наш HttpService использует функцию токена отмены от Axios, которая концептуально похожа на AbortController из Fetch API. Идея состоит в том, чтобы предоставить связанный метод cancel вместе с каждым запросом. При необходимости вызов этого метода cancel прерывает HTTP-запрос.

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

Рассмотрим компонент React, который получает список задач:

import React, { useState, useEffect } from 'react';

function TodoList() {
    const [todos, setTodos] = useState([]);
    
    useEffect(() => {
        const apiService = new HttpService();
        const { request, cancel } = apiService.get('/todos');

        request.then(data => {
            setTodos(data);
        }).catch(error => {
            if (axios.isCancel(error)) {
                console.log("Request canceled:", error.message);
            } else {
                // Handle other errors
            }
        });
        
        return () => {
            // Cleanup logic: if the component unmounts before the request completes, cancel the request
            cancel("Component unmounted, aborting request");
        };
    }, []);

    return (
        <ul>
            {todos.map(todo => <li key={todo.id}>{todo.title}</li>)}
        </ul>
    );
}

В этом примере, если компонент TodoList размонтируется до разрешения нашего запроса (возможно, из-за изменения навигации), мы используем метод cancel, чтобы прервать его, гарантируя отсутствие непредвиденных обновлений состояния или эффектов.

Шаг 7. Настройка базового URL

Для случаев, когда вам нужно переключить конечную точку API:

setBaseUrl(newUrl) {
    this.baseUrl = newUrl;
    this.instance.defaults.baseURL = newUrl;
}

Подведение итогов

С помощью этого HttpService вы можете централизовать логику HTTP-запросов, сделав ваши компоненты React чище:

const apiService = new HttpService();

// Somewhere in a React component:
apiService.get('/todos').request.then(data => {
    // Handle data
});

Забегая вперед: потенциальные улучшения для HttpService

Хотя созданный нами HttpService прекрасно отвечает нашим текущим требованиям, его архитектура рассчитана на масштабируемость. По мере развития нашего приложения и возникновения сложностей сервис может расширяться.

Вот пример того, что еще можно интегрировать:

  • Время ожидания запроса. Определенное время ожидания для отдельных запросов.
  • Перехватчики: более глубокое понимание запросов и ответов до их обработки или отправки.
  • Единая обработка ошибок: централизованный подход к управлению ошибками.
  • Управление заголовками: динамические методы для изменения заголовков по умолчанию в течение жизненного цикла приложения.

Гибкость шаблона фасада гарантирует, что наш HttpService останется адаптируемым, готовым к улучшениям без нарушения структуры нашего приложения.

Преимущества и заключение

Используя шаблон фасада с нашим HttpService:

  1. Чистый код. Наши компоненты остаются чистыми и сосредоточены на рендеринге.
  2. Гибкость. Нужно переключиться на другой HTTP-клиент? Измените HttpService, не касаясь кода компонента.
  3. Согласованность. Заголовки и конфигурации остаются неизменными.

Создание удобных в сопровождении приложений требует предусмотрительности и шаблонов проектирования. Наш HttpService — это один шаг в правильном направлении.

И вот оно! Пошаговое руководство по созданию модульного HttpService приложения React с использованием шаблона Facade.