# Создание банковского приложения, часть 3: методы получения и использования данных Представьте себе компьютер на борту корабля Enterprise из «Звездного пути» — когда капитан Пикар запрашивает статус корабля, информация появляется мгновенно, без перезагрузки интерфейса. Именно такой плавный поток данных мы создаем здесь с помощью динамического получения данных. Сейчас ваше банковское приложение похоже на печатную газету — информативно, но статично. Мы превратим его в нечто похожее на центр управления полетами NASA, где данные поступают непрерывно и обновляются в реальном времени, не прерывая работу пользователя. Вы научитесь асинхронно взаимодействовать с серверами, обрабатывать данные, которые поступают в разное время, и преобразовывать сырую информацию в полезную для пользователей. Это и есть разница между демонстрационной версией и готовым к производству программным обеспечением. ## Тест перед лекцией [Тест перед лекцией](https://ff-quizzes.netlify.app/web/quiz/45) ### Предварительные требования Перед тем как углубиться в получение данных, убедитесь, что у вас готовы следующие компоненты: - **Предыдущий урок**: Завершите [форму входа и регистрации](../2-forms/README.md) — мы будем строить на этой основе. - **Локальный сервер**: Установите [Node.js](https://nodejs.org) и [запустите сервер API](../api/README.md), чтобы предоставить данные аккаунта. - **Соединение с API**: Проверьте соединение с сервером с помощью этой команды: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Этот быстрый тест гарантирует, что все компоненты правильно взаимодействуют: - Проверяет, что Node.js работает корректно на вашей системе. - Подтверждает, что ваш сервер API активен и отвечает. - Убеждает, что ваше приложение может связаться с сервером (как проверка радиосвязи перед миссией). --- ## Понимание получения данных в современных веб-приложениях Способы обработки данных веб-приложениями значительно изменились за последние два десятилетия. Понимание этой эволюции поможет вам оценить, почему современные методы, такие как AJAX и Fetch API, настолько мощны и почему они стали необходимыми инструментами для веб-разработчиков. Давайте рассмотрим, как работали традиционные веб-сайты по сравнению с динамическими, отзывчивыми приложениями, которые мы создаем сегодня. ### Традиционные многостраничные приложения (MPA) В первые дни веба каждый клик был похож на переключение каналов на старом телевизоре — экран становился пустым, а затем медленно загружал новый контент. Это была реальность ранних веб-приложений, где каждое взаимодействие означало полную перезагрузку страницы. ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: Clicks link or submits form Browser->>Server: Requests new HTML page Note over Browser: Page goes blank Server->>Browser: Returns complete HTML page Browser->>User: Displays new page (flash/reload) ```  **Почему этот подход казался неудобным:** - Каждый клик означал полную перезагрузку страницы. - Пользователи прерывались на середине мысли из-за раздражающих миганий страницы. - Ваше интернет-соединение работало на пределе, загружая один и тот же заголовок и нижний колонтитул снова и снова. - Приложения больше походили на просмотр картотеки, чем на использование программного обеспечения. ### Современные одностраничные приложения (SPA) AJAX (асинхронный JavaScript и XML) полностью изменил эту парадигму. Как модульная конструкция Международной космической станции, где астронавты могут заменять отдельные компоненты без перестройки всей структуры, AJAX позволяет обновлять конкретные части веб-страницы без перезагрузки всего. Несмотря на то, что в названии упоминается XML, сегодня мы в основном используем JSON, но основной принцип остается: обновлять только то, что нужно. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interacts with page Browser->>JavaScript: Triggers event handler JavaScript->>Server: Fetches only needed data Server->>JavaScript: Returns JSON data JavaScript->>Browser: Updates specific page elements Browser->>User: Shows updated content (no reload) ```  **Почему SPA намного удобнее:** - Обновляются только те части, которые действительно изменились (умно, правда?). - Больше никаких резких прерываний — пользователи остаются в потоке. - Меньше данных передается по сети, что означает более быструю загрузку. - Все кажется быстрым и отзывчивым, как приложения на вашем телефоне. ### Эволюция к современному Fetch API Современные браузеры предоставляют [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), который заменяет устаревший [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Как разница между использованием телеграфа и электронной почты, Fetch API использует промисы для более чистого асинхронного кода и естественно обрабатывает JSON. | Функция | XMLHttpRequest | Fetch API | |---------|----------------|----------| | **Синтаксис** | Сложный, основанный на обратных вызовах | Чистый, основанный на промисах | | **Обработка JSON** | Требуется ручное парсирование | Встроенный метод `.json()` | | **Обработка ошибок** | Ограниченная информация об ошибках | Полная информация об ошибках | | **Современная поддержка** | Совместимость с устаревшими системами | ES6+ промисы и async/await | > 💡 **Совместимость с браузерами**: Хорошие новости — Fetch API работает во всех современных браузерах! Если вам интересно узнать о конкретных версиях, [caniuse.com](https://caniuse.com/fetch) предоставит полную информацию о совместимости. > **Итог:** - Отлично работает в Chrome, Firefox, Safari и Edge (везде, где находятся ваши пользователи). - Только Internet Explorer требует дополнительной помощи (и честно говоря, пора отпустить IE). - Идеально подходит для элегантных async/await паттернов, которые мы будем использовать позже. ### Реализация входа пользователя и получения данных Теперь давайте реализуем систему входа, которая преобразует ваше банковское приложение из статического дисплея в функциональное приложение. Как протоколы аутентификации, используемые в защищенных военных объектах, мы проверим учетные данные пользователя, а затем предоставим доступ к его конкретным данным. Мы будем строить это постепенно, начиная с базовой аутентификации и затем добавляя возможности получения данных. #### Шаг 1: Создание основы функции входа Откройте ваш файл `app.js` и добавьте новую функцию `login`. Она будет обрабатывать процесс аутентификации пользователя: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Разберем это:** - Ключевое слово `async` говорит JavaScript: «Эй, эта функция может потребовать ожидания». - Мы находим форму на странице (ничего сложного, просто ищем ее по ID). - Затем извлекаем то, что пользователь ввел в качестве имени пользователя. - Вот хитрость: вы можете получить доступ к любому элементу формы через его атрибут `name` — не нужно дополнительных вызовов getElementById! > 💡 **Шаблон доступа к форме**: Каждый элемент управления формой может быть доступен через его имя (установленное в HTML с помощью атрибута `name`) как свойство элемента формы. Это обеспечивает чистый и удобочитаемый способ получения данных формы. #### Шаг 2: Создание функции получения данных аккаунта Далее мы создадим отдельную функцию для получения данных аккаунта с сервера. Это следует той же схеме, что и ваша функция регистрации, но сосредоточено на получении данных: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **Что делает этот код:** - **Использует** современный `fetch` API для асинхронного запроса данных. - **Формирует** URL-адрес GET-запроса с параметром имени пользователя. - **Применяет** `encodeURIComponent()` для безопасной обработки специальных символов в URL. - **Преобразует** ответ в формат JSON для удобной манипуляции данными. - **Обрабатывает** ошибки аккуратно, возвращая объект ошибки вместо сбоя. > ⚠️ **Примечание по безопасности**: Функция `encodeURIComponent()` обрабатывает специальные символы в URL. Как системы кодирования, используемые в военно-морских коммуникациях, она гарантирует, что ваше сообщение прибудет точно так, как задумано, предотвращая неправильную интерпретацию символов, таких как "#" или "&". > **Почему это важно:** - Предотвращает сбои URL из-за специальных символов. - Защищает от атак с манипуляцией URL. - Гарантирует, что сервер получает данные в нужном виде. - Следует принципам безопасного кодирования. #### Понимание HTTP GET-запросов Вот что может вас удивить: когда вы используете `fetch` без дополнительных опций, он автоматически создает [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) запрос. Это идеально подходит для того, что мы делаем — спрашиваем у сервера: «Эй, можно мне посмотреть данные аккаунта этого пользователя?» Думайте о GET-запросах как о вежливой просьбе взять книгу в библиотеке — вы запрашиваете доступ к тому, что уже существует. POST-запросы (которые мы использовали для регистрации) больше похожи на отправку новой книги для добавления в коллекцию. | GET-запрос | POST-запрос | |------------|-------------| | **Цель** | Получение существующих данных | Отправка новых данных на сервер | | **Параметры** | В пути URL/строке запроса | В теле запроса | | **Кэширование** | Может быть кэшировано браузерами | Обычно не кэшируется | | **Безопасность** | Видно в URL/логах | Скрыто в теле запроса | #### Шаг 3: Объединение всех частей Теперь самая приятная часть — давайте подключим вашу функцию получения данных аккаунта к процессу входа. Здесь все становится на свои места: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` Эта функция следует четкой последовательности: - Извлекает имя пользователя из ввода формы. - Запрашивает данные аккаунта пользователя с сервера. - Обрабатывает любые ошибки, возникающие в процессе. - Сохраняет данные аккаунта и переходит на панель управления в случае успеха. > 🎯 **Паттерн Async/Await**: Поскольку `getAccount` является асинхронной функцией, мы используем ключевое слово `await`, чтобы приостановить выполнение до ответа сервера. Это предотвращает продолжение кода с неопределенными данными. #### Шаг 4: Создание хранилища для данных Вашему приложению нужно место, где оно будет хранить информацию об аккаунте после загрузки. Думайте об этом как о краткосрочной памяти вашего приложения — месте, где можно держать данные текущего пользователя под рукой. Добавьте эту строку в начало вашего файла `app.js`: ```javascript // This holds the current user's account data let account = null; ``` **Почему это нужно:** - Держит данные аккаунта доступными из любой части вашего приложения. - Начало с `null` означает, что «никто еще не вошел». - Обновляется, когда кто-то успешно входит или регистрируется. - Действует как единый источник правды — никакой путаницы о том, кто вошел. #### Шаг 5: Подключение формы Теперь давайте подключим вашу новую функцию входа к HTML-форме. Обновите тег формы следующим образом: ```html
``` **Что делает это небольшое изменение:** - Останавливает форму от выполнения ее стандартного поведения «перезагрузить всю страницу». - Вызывает вашу пользовательскую JavaScript-функцию вместо этого. - Сохраняет все плавным и в стиле одностраничного приложения. - Дает вам полный контроль над тем, что происходит, когда пользователи нажимают «Войти». #### Шаг 6: Улучшение функции регистрации Для согласованности обновите вашу функцию `register`, чтобы также сохранять данные аккаунта и переходить на панель управления: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Это улучшение обеспечивает:** - **Плавный** переход от регистрации к панели управления. - **Согласованный** пользовательский опыт между процессами входа и регистрации. - **Мгновенный** доступ к данным аккаунта после успешной регистрации. #### Тестирование реализации ```mermaid flowchart TD A[User enters credentials] --> B[Login function called] B --> C[Fetch account data from server] C --> D{Data received successfully?} D -->|Yes| E[Store account data globally] D -->|No| F[Display error message] E --> G[Navigate to dashboard] F --> H[User stays on login page] ``` **Пора протестировать:** 1. Создайте новый аккаунт, чтобы убедиться, что все работает. 2. Попробуйте войти с этими же учетными данными. 3. Загляните в консоль браузера (F12), если что-то кажется неправильным. 4. Убедитесь, что вы попадаете на панель управления после успешного входа. Если что-то не работает, не паникуйте! Большинство проблем легко исправить, например, опечатки или забытый запуск API-сервера. #### Несколько слов о магии междоменного взаимодействия Вы можете задаться вопросом: «Как мое веб-приложение общается с этим сервером API, если они работают на разных портах?» Отличный вопрос! Это касается того, с чем сталкивается каждый веб-разработчик. > 🔒 **Безопасность междоменного взаимодействия**: Браузеры применяют политику «одного источника», чтобы предотвратить несанкционированное взаимодействие между различными доменами. Как система пропусков в Пентагоне, они проверяют, что взаимодействие авторизовано, прежде чем разрешить передачу данных. > **В нашей настройке:** - Ваше веб-приложение работает на `localhost:3000` (сервер разработки). - Ваш сервер API работает на `localhost:5000` (сервер бэкэнда). - Сервер API включает [заголовки CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS), которые явно разрешают взаимодействие с вашим веб-приложением. Эта конфигурация отражает реальную разработку, где фронтенд и бэкэнд приложения обычно работают на отдельных серверах. > 📚 **Узнать больше**: Узнайте больше о API и получении данных с этим подробным [модулем Microsoft Learn об API](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Превращение данных в HTML Теперь мы сделаем полученные данные видимыми для пользователей через манипуляцию DOM. Как процесс проявления фотографий в фотолаборатории, мы превращаем невидимые данные в то, что пользователи могут видеть и с чем взаимодействовать. Манипуляция DOM — это техника, которая преобразует статические веб-страницы в динамические приложения, обновляющие свой контент на основе взаимодействий пользователей и ответов сервера. ### Выбор подходящего инструмента для работы Когда дело доходит до обновления вашего HTML с помощью JavaScript, у вас есть несколько вариантов. Думайте о них как о разных инструментах в наборе — каждый идеально подходит для определенных задач: | Метод | Для чего подходит | Когда использовать | Уровень безопасности | |-------|-------------------|--------------------|-----------------------| | `textContent` | Безопасное отображение данных пользователя | В любое время, когда вы показываете текст | ✅ Надежно | | `createElement()` + `append()` | Создание сложных макетов | Создание новых разделов/списков | ✅ Безопасно | | `innerHTML` | Установка HTML-контента | ⚠️ Старайтесь избегать | ❌ Рискованно | #### Безопасный способ отображения текста: textContent Свойство [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) — ваш лучший друг при отображении данных пользователя. Это как охранник для вашей веб-страницы — ничего вредного не пройдет: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Преимущества textContent:** - Обрабатывает все как простой текст (предотвращает выполнение скриптов). - Автоматически очищает существующий контент. - Эффективен для простых текстовых обновлений. - Обеспечивает встроенную защиту от вредоносного контента. #### Создание динамических HTML-элементов Для более сложного контента объедините [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) с методом [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append): ```javascript // Safe way to create new elements const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **Понимание подхода:** - **Создание** новых элементов DOM программным способом - **Полный контроль** над атрибутами и содержимым элементов - **Возможность** создания сложных, вложенных структур элементов - **Обеспечение безопасности** за счет разделения структуры и содержимого > ⚠️ **Соображения безопасности**: Хотя [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) часто встречается в учебных материалах, он может выполнять встроенные скрипты. Как и протоколы безопасности в CERN, которые предотвращают выполнение несанкционированного кода, использование `textContent` и `createElement` является более безопасной альтернативой. > **Риски использования innerHTML:** - Выполняет любые теги `