# Создание банковского приложения, часть 3: методы получения и использования данных Представьте себе компьютер на корабле Enterprise из "Звездного пути" — когда капитан Пикар запрашивает статус корабля, информация появляется мгновенно, без перезагрузки интерфейса. Именно такой плавный поток информации мы создаем здесь с помощью динамического получения данных. Сейчас ваше банковское приложение похоже на печатную газету — информативно, но статично. Мы превратим его во что-то вроде центра управления полетами NASA, где данные поступают непрерывно и обновляются в реальном времени, не прерывая работу пользователя. Вы научитесь асинхронно взаимодействовать с серверами, обрабатывать данные, которые поступают в разное время, и преобразовывать сырую информацию в полезную для ваших пользователей. Это разница между демонстрацией и готовым к производству программным обеспечением. ## ⚡ Что можно сделать за следующие 5 минут **Быстрый старт для занятых разработчиков** ```mermaid flowchart LR A[⚡ 5 minutes] --> B[Set up API server] B --> C[Test fetch with curl] C --> D[Create login function] D --> E[See data in action] ``` - **Минута 1-2**: Запустите сервер API (`cd api && npm start`) и протестируйте соединение - **Минута 3**: Создайте базовую функцию `getAccount()` с использованием fetch - **Минута 4**: Подключите форму входа с `action="javascript:login()"` - **Минута 5**: Протестируйте вход и посмотрите, как данные аккаунта появляются в консоли **Команды для быстрого тестирования**: ```bash # Verify API is running curl http://localhost:5000/api # Test account data fetch curl http://localhost:5000/api/accounts/test ``` **Почему это важно**: За 5 минут вы увидите магию асинхронного получения данных, которая лежит в основе каждого современного веб-приложения. Это фундамент, который делает приложения отзывчивыми и живыми. ## 🗺️ Ваш путь к изучению веб-приложений, управляемых данными ```mermaid journey title From Static Pages to Dynamic Applications section Understanding the Evolution Traditional page reloads: 3: You Discover AJAX/SPA benefits: 5: You Master Fetch API patterns: 7: You section Building Authentication Create login functions: 4: You Handle async operations: 6: You Manage user sessions: 8: You section Dynamic UI Updates Learn DOM manipulation: 5: You Build transaction displays: 7: You Create responsive dashboards: 9: You section Professional Patterns Template-based rendering: 6: You Error handling strategies: 7: You Performance optimization: 8: You ``` **Цель вашего пути**: К концу этого урока вы поймете, как современные веб-приложения получают, обрабатывают и динамически отображают данные, создавая плавный пользовательский опыт, который мы ожидаем от профессиональных приложений. ## Предварительный тест [Предварительный тест](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 активен и отвечает - Убеждает, что ваше приложение может достичь сервера (как проверка радиосвязи перед миссией) ## 🧠 Обзор экосистемы управления данными ```mermaid mindmap root((Data Management)) Authentication Flow Login Process Form Validation Credential Verification Session Management User State Global Account Object Navigation Guards Error Handling API Communication Fetch Patterns GET Requests POST Requests Error Responses Data Formats JSON Processing URL Encoding Response Parsing Dynamic UI Updates DOM Manipulation Safe Text Updates Element Creation Template Cloning User Experience Real-time Updates Error Messages Loading States Security Considerations XSS Prevention textContent Usage Input Sanitization Safe HTML Creation CORS Handling Cross-Origin Requests Header Configuration Development Setup ``` **Основной принцип**: Современные веб-приложения — это системы оркестрации данных, которые координируют взаимодействие между пользовательскими интерфейсами, серверными 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/логах | Скрыто в теле запроса | ```mermaid sequenceDiagram participant B as Browser participant S as Server Note over B,S: GET Request (Data Retrieval) B->>S: GET /api/accounts/test S-->>B: 200 OK + Account Data Note over B,S: POST Request (Data Submission) B->>S: POST /api/accounts + New Account Data S-->>B: 201 Created + Confirmation Note over B,S: Error Handling B->>S: GET /api/accounts/nonexistent S-->>B: 404 Not Found + Error Message ``` #### Шаг 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:** - Выполняет любые теги `