Bài 31.5: Bài tập thực hành hệ thống authentication

Chào mừng bạn đến với bài thực hành quan trọng bậc nhất trong chuỗi bài về authentication! Chúng ta đã cùng nhau đi qua những khái niệm lý thuyết, hiểu về vai trò, các thành phần và luồng hoạt động của một hệ thống xác thực. Tuy nhiên, lý thuyết dù vững vàng đến đâu cũng cần được biến thành hành động để thực sự nắm vững và áp dụng vào thực tế.

Bài viết này tập trung vào việc cung cấp cho bạn các bài tập thực hành cụ thể để xây dựng các phần của hệ thống authentication. Đây là cơ hội để bạn đối mặt với những vấn đề thực tế, viết code, debug và xây dựng sự tự tin khi làm việc với một trong những khía cạnh quan trọng nhất của ứng dụng web: bảo mậtquản lý người dùng.

Hãy cùng bắt tay vào làm nào!

Tại Sao Thực Hành Authentication Lại Quan Trọng Đến Thế?

Authentication không chỉ là việc tạo ra form đăng nhập và đăng ký. Nó liên quan đến:

  1. Bảo mật: Ngăn chặn truy cập trái phép, bảo vệ dữ liệu người dùng.
  2. Trải nghiệm người dùng: Quản lý trạng thái đăng nhập, cá nhân hóa nội dung.
  3. Tích hợp hệ thống: Kết nối front-end với back-end một cách an toàn.
  4. Xử lý lỗi: Đối phó với các trường hợp như sai mật khẩu, tài khoản không tồn tại, lỗi mạng...

Việc thực hành sẽ giúp bạn:

  • Hiểu sâu sắc hơn về luồng dữ liệu giữa client (front-end) và server (back-end).
  • Nắm vững cách sử dụng các công nghệ như Fetch API hoặc Axios để giao tiếp với API.
  • Học cách quản lý trạng thái đăng nhập trong ứng dụng front-end (sử dụng Context API, Redux, Zustand, hoặc đơn giản là useState/useReducer).
  • Biết cách bảo vệ các route (trang) yêu cầu người dùng đã đăng nhập.
  • Thực hành xử lý lỗi và hiển thị thông báo phù hợp cho người dùng.

Hãy coi đây là thử thách để củng cố kiến thức của bạn!

Các Bài Tập Thực Hành Trọng Tâm

Chúng ta sẽ chia nhỏ hệ thống authentication thành các phần để dễ dàng thực hành. Đối với các bài tập này, bạn có thể tự xây dựng một back-end đơn giản (sử dụng Node.js/Express, Python/Flask, etc.) hoặc sử dụng các dịch vụ BaaS (Backend-as-a-Service) như Firebase Authentication, Supabase Auth, hoặc Auth0 để tập trung vào phần front-end. Nếu bạn chưa học về back-end, việc sử dụng BaaS là một lựa chọn tuyệt vời.

Giả định rằng bạn có các API endpoint cơ bản cho đăng ký, đăng nhập và lấy thông tin người dùng.

Bài Tập 1: Xây dựng Giao diện Đăng ký (Registration UI)

Mục tiêu: Tạo form cho phép người dùng nhập thông tin để tạo tài khoản mới.

Các bước thực hiện:

  1. Tạo một trang hoặc component Register.
  2. Thiết kế form với các trường nhập liệu cần thiết:
    • Tên người dùng (Username) hoặc Email
    • Mật khẩu (Password)
    • Xác nhận mật khẩu (Confirm Password)
    • (Tùy chọn) Các trường khác như Tên đầy đủ, v.v.
  3. Sử dụng các thành phần HTML (<form>, <input>, <label>, <button>).
  4. (Nâng cao) Áp dụng CSS để form trông thân thiện và dễ sử dụng.

Code minh họa (HTML Structure):

<form id="registrationForm">
    <h2>Đăng Ký Tài Khoản Mới</h2>
    <div>
        <label for="username">Tên người dùng:</label>
        <input type="text" id="username" name="username" required>
    </div>
    <div>
        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required>
    </div>
    <div>
        <label for="password">Mật khẩu:</label>
        <input type="password" id="password" name="password" required>
    </div>
    <div>
        <label for="confirmPassword">Xác nhận mật khẩu:</label>
        <input type="password" id="confirmPassword" name="confirmPassword" required>
    </div>
    <button type="submit">Đăng Ký</button>
</form>
  • Giải thích: Đoạn code này chỉ là cấu trúc HTML cơ bản của một form đăng ký. Chúng ta sử dụng các input với type phù hợp (text, email, password) và thuộc tính required để yêu cầu người dùng nhập dữ liệu. idname rất quan trọng cho việc xử lý dữ liệu sau này.
Bài Tập 2: Xây dựng Giao diện Đăng nhập (Login UI)

Mục tiêu: Tạo form cho phép người dùng nhập thông tin để đăng nhập vào hệ thống.

Các bước thực hiện:

  1. Tạo một trang hoặc component Login.
  2. Thiết kế form với các trường nhập liệu:
    • Tên người dùng/Email
    • Mật khẩu
  3. Sử dụng các thành phần HTML tương tự như form đăng ký.
  4. (Nâng cao) Thêm liên kết "Quên mật khẩu?" và "Chưa có tài khoản? Đăng ký ngay".

Code minh họa (HTML Structure):

<form id="loginForm">
    <h2>Đăng Nhập</h2>
    <div>
        <label for="login-username">Tên người dùng hoặc Email:</label>
        <input type="text" id="login-username" name="username" required>
    </div>
    <div>
        <label for="login-password">Mật khẩu:</label>
        <input type="password" id="login-password" name="password" required>
    </div>
    <button type="submit">Đăng Nhập</button>
    <p><a href="#">Quên mật khẩu?</a></p>
    <p>Chưa có tài khoản? <a href="/register">Đăng ký ngay</a></p>
</form>
  • Giải thích: Tương tự như form đăng ký, đây là cấu trúc HTML cho form đăng nhập. Điểm khác biệt là số lượng trường nhập liệu ít hơn và có thêm các liên kết phụ trợ.
Bài Tập 3: Xử lý Logic Đăng ký và Kết nối API

Mục tiêu: Bắt sự kiện submit form đăng ký, lấy dữ liệu từ form, và gửi yêu cầu (request) đến API back-end để tạo tài khoản mới.

Các bước thực hiện:

  1. Sử dụng JavaScript (hoặc TypeScript, React/Next.js hook) để bắt sự kiện submit của form đăng ký.
  2. Ngăn chặn hành vi submit mặc định của trình duyệt (event.preventDefault()).
  3. Lấy giá trị từ các trường input.
  4. (Quan trọng) Kiểm tra tính hợp lệ của dữ liệu (client-side validation): mật khẩu có khớp không, định dạng email, độ dài mật khẩu...
  5. Nếu dữ liệu hợp lệ, tạo một đối tượng chứa dữ liệu đăng ký.
  6. Sử dụng Fetch API hoặc thư viện như Axios để gửi yêu cầu POST đến endpoint đăng ký của back-end.
  7. Xử lý kết quả từ API:
    • Nếu thành công (ví dụ: status code 200 hoặc 201): Thông báo cho người dùng đăng ký thành công, có thể tự động chuyển hướng đến trang đăng nhập.
    • Nếu thất bại (ví dụ: status code 400, 500): Hiển thị thông báo lỗi từ back-end cho người dùng (ví dụ: "Tên người dùng đã tồn tại", "Mật khẩu quá yếu",...).

Code minh họa (JavaScript Fetch API):

const registrationForm = document.getElementById('registrationForm');

registrationForm.addEventListener('submit', async (event) => {
    event.preventDefault(); // Ngăn chặn submit mặc định

    const username = document.getElementById('username').value;
    const email = document.getElementById('email').value;
    const password = document.getElementById('password').value;
    const confirmPassword = document.getElementById('confirmPassword').value;

    // TODO: Thêm client-side validation ở đây (kiểm tra mật khẩu khớp, định dạng email, vv)
    if (password !== confirmPassword) {
        alert("Mật khẩu và xác nhận mật khẩu không khớp!");
        return;
    }

    const registrationData = { username, email, password };
    const apiUrl = 'YOUR_BACKEND_REGISTER_API_URL'; // Thay bằng URL API thật của bạn

    try {
        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(registrationData),
        });

        const result = await response.json(); // Parse JSON response

        if (response.ok) { // response.ok là true nếu status code là 2xx
            console.log('Đăng ký thành công:', result);
            alert('Đăng ký thành công! Vui lòng đăng nhập.');
            // TODO: Chuyển hướng người dùng đến trang đăng nhập
            // window.location.href = '/login';
        } else {
            console.error('Đăng ký thất bại:', result);
            alert('Đăng ký thất bại: ' + (result.message || 'Có lỗi xảy ra.')); // Hiển thị lỗi từ server
        }

    } catch (error) {
        console.error('Lỗi mạng hoặc hệ thống:', error);
        alert('Đã xảy ra lỗi khi kết nối đến máy chủ.');
    }
});
  • Giải thích: Đoạn script này minh họa cách sử dụng JavaScript để xử lý form. Chúng ta bắt sự kiện submit, lấy dữ liệu form, thực hiện một kiểm tra đơn giản (kiểm tra mật khẩu khớp), sau đó dùng fetch để gửi dữ liệu lên server. Phương thức POST, header Content-Type: application/json, và JSON.stringify để gửi dữ liệu dưới dạng JSON là các phần cốt lõi khi giao tiếp API. Chúng ta cũng xử lý phản hồi từ server dựa vào response.ok và hiển thị thông báo tương ứng.
Bài Tập 4: Xử lý Logic Đăng nhập và Lưu Token/Session

Mục tiêu: Bắt sự kiện submit form đăng nhập, gửi thông tin đăng nhập đến API, và xử lý phản hồi thành công bằng cách lưu token (ví dụ: JWT) hoặc thông tin session vào trình duyệt.

Các bước thực hiện:

  1. Sử dụng JavaScript (hoặc TypeScript, React/Next.js hook) để bắt sự kiện submit của form đăng nhập.
  2. Ngăn chặn submit mặc định.
  3. Lấy giá trị từ các trường input (username/email, password).
  4. Sử dụng Fetch API hoặc Axios để gửi yêu cầu POST đến endpoint đăng nhập của back-end.
  5. Xử lý kết quả từ API:
    • Nếu thành công (ví dụ: status code 200): API sẽ trả về token (như JWT) hoặc thông tin session. Lưu trữ token này vào nơi an toàn trong trình duyệt (ví dụ: localStorage, sessionStorage, hoặc cookies - cần hiểu rõ sự khác nhau và bảo mật của từng phương pháp). Cập nhật trạng thái đăng nhập trong ứng dụng và chuyển hướng người dùng đến trang riêng tư (ví dụ: dashboard).
    • Nếu thất bại (ví dụ: status code 401, 400): Hiển thị thông báo lỗi cho người dùng (ví dụ: "Sai tên đăng nhập hoặc mật khẩu").

Code minh họa (JavaScript Fetch API & localStorage):

const loginForm = document.getElementById('loginForm');

loginForm.addEventListener('submit', async (event) => {
    event.preventDefault(); // Ngăn chặn submit mặc định

    const username = document.getElementById('login-username').value;
    const password = document.getElementById('login-password').value;

    const loginData = { username, password };
    const apiUrl = 'YOUR_BACKEND_LOGIN_API_URL'; // Thay bằng URL API thật của bạn

    try {
        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(loginData),
        });

        const result = await response.json();

        if (response.ok) {
            console.log('Đăng nhập thành công:', result);
            // Giả sử API trả về một đối tượng có thuộc tính 'token'
            const token = result.token;
            if (token) {
                // Lưu token vào localStorage (LƯU Ý: Cần hiểu rõ về bảo mật khi dùng localStorage)
                localStorage.setItem('authToken', token);
                alert('Đăng nhập thành công!');
                // TODO: Cập nhật trạng thái đăng nhập trong ứng dụng front-end
                // TODO: Chuyển hướng người dùng đến trang dashboard hoặc trang khác
                // window.location.href = '/dashboard';
            } else {
                 alert('Đăng nhập thành công nhưng không nhận được token.');
            }
        } else {
            console.error('Đăng nhập thất bại:', result);
            alert('Đăng nhập thất bại: ' + (result.message || 'Sai tên đăng nhập hoặc mật khẩu.'));
        }

    } catch (error) {
        console.error('Lỗi mạng hoặc hệ thống:', error);
        alert('Đã xảy ra lỗi khi kết nối đến máy chủ.');
    }
});
  • Giải thích: Code này gần giống với logic đăng ký nhưng điểm then chốt là khi nhận được phản hồi thành công (response.ok), chúng ta lấy token từ dữ liệu trả về (result.token) và lưu nó vào localStorage của trình duyệt. localStorage.setItem('authToken', token) là cách đơn giản nhất để lưu trữ, mặc dù cần cân nhắc các khía cạnh bảo mật nâng cao hơn cho ứng dụng thực tế. Sau khi lưu token, ứng dụng front-end của bạn sẽ "nhận biết" được người dùng đã đăng nhập.
Bài Tập 5: Quản lý State Xác thực và Bảo vệ Route

Mục tiêu: Dựa vào token/session đã lưu, ứng dụng front-end có thể biết người dùng có đang đăng nhập hay không và điều chỉnh giao diện, cũng như ngăn chặn truy cập vào các trang chỉ dành cho người dùng đã xác thực.

Các bước thực hiện:

  1. Khi ứng dụng khởi động hoặc khi state cần kiểm tra, đọc token từ localStorage (hoặc nơi bạn đã lưu).
  2. Tạo một "state" hoặc "context" toàn cục để lưu trữ trạng thái đăng nhập (ví dụ: isLoggedIn: boolean, userData: object).
  3. Tạo một hàm hoặc hook để kiểm tra xem người dùng có đang đăng nhập không (kiểm tra sự tồn tại của token).
  4. Trên các component hoặc route yêu cầu đăng nhập (ví dụ: trang profile, dashboard, settings), sử dụng hàm/hook kiểm tra trạng thái đăng nhập:
    • Nếu đã đăng nhập: Hiển thị nội dung trang.
    • Nếu chưa đăng nhập: Chuyển hướng người dùng về trang đăng nhập hoặc hiển thị thông báo lỗi.
  5. (Nâng cao) Khi thực hiện các request API cần xác thực (ví dụ: lấy thông tin user, post bài viết), thêm token vào header của request (thường là header Authorization với giá trị dạng Bearer YOUR_TOKEN).

Code minh họa (JavaScript & Pseudocode cho Route Protection):

// --- Quản lý State Đăng nhập (Ví dụ đơn giản) ---
function checkAuthStatus() {
    const token = localStorage.getItem('authToken');
    if (token) {
        // TODO: (Nâng cao) Kiểm tra token có hợp lệ/hết hạn không bằng cách gọi API kiểm tra hoặc giải mã JWT (client-side decoding không kiểm tra tính hợp lệ server)
        console.log("Người dùng đã đăng nhập.");
        return true; // Giả định token tồn tại là đã đăng nhập
    } else {
        console.log("Người dùng chưa đăng nhập.");
        return false;
    }
}

// --- Ví dụ Pseudocode bảo vệ Route (Giả định dùng React Router hoặc tương tự) ---
// Đây không phải code chạy ngay, mà là ý tưởng logic
/*
function PrivateRoute({ component: Component, ...rest }) {
    const isAuthenticated = checkAuthStatus(); // Kiểm tra trạng thái

    return (
        <Route
            {...rest}
            render={props =>
                isAuthenticated ? (
                    <Component {...props} /> // Nếu đăng nhập, render component
                ) : (
                    // Nếu chưa đăng nhập, chuyển hướng về trang login
                    <Redirect to="/login" />
                )
            }
        />
    );
}

// Cách sử dụng trong định tuyến ứng dụng:
// <PrivateRoute path="/dashboard" component={DashboardPage} />
// <Route path="/login" component={LoginPage} />
*/

// --- Ví dụ thêm Token vào API Request (Sử dụng Fetch API) ---
async function fetchUserData() {
    const token = localStorage.getItem('authToken');
    const apiUrl = 'YOUR_BACKEND_USER_DATA_API_URL'; // Thay bằng URL API lấy data user

    if (!token) {
        console.error("Không tìm thấy token. Người dùng chưa đăng nhập.");
        // TODO: Chuyển hướng về trang login
        return;
    }

    try {
        const response = await fetch(apiUrl, {
            method: 'GET', // Hoặc phương thức khác tùy API
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`, // Thêm header Authorization với token
            },
        });

        if (response.ok) {
            const userData = await response.json();
            console.log('Dữ liệu người dùng:', userData);
            // TODO: Cập nhật UI với dữ liệu người dùng
        } else if (response.status === 401 || response.status === 403) {
            console.error('Token không hợp lệ hoặc hết hạn.');
            // TODO: Xóa token cũ, cập nhật trạng thái đăng xuất, chuyển hướng về login
            // localStorage.removeItem('authToken');
            // alert('Phiên đăng nhập đã hết hạn. Vui lòng đăng nhập lại.');
            // window.location.href = '/login';
        } else {
            console.error('Lỗi khi lấy dữ liệu người dùng:', await response.text());
             alert('Lỗi khi lấy dữ liệu người dùng.');
        }
    } catch (error) {
         console.error('Lỗi mạng hoặc hệ thống:', error);
         alert('Đã xảy ra lỗi khi kết nối.');
    }
}

// Gọi hàm này khi cần lấy dữ liệu user sau khi đăng nhập
// fetchUserData();
  • Giải thích: Phần này giới thiệu cách kiểm tra trạng thái đăng nhập bằng cách đọc token từ localStorage. Hàm checkAuthStatus là ví dụ đơn giản nhất. Đoạn pseudocode PrivateRoute minh họa cách các thư viện định tuyến (routing libraries) trong React, Vue, hoặc Angular thường xử lý việc bảo vệ route: kiểm tra điều kiện trước khi cho phép truy cập. Quan trọng không kém là cách thêm token vào header Authorization khi gọi các API cần xác thực, đây là cách phổ biến để back-end nhận biết request này đến từ người dùng nào và có quyền truy cập hay không. Việc xử lý response 401/403 khi token không hợp lệ cũng rất quan trọng.
Bài Tập 6: Xử lý Đăng xuất (Logout)

Mục tiêu: Cho phép người dùng đăng xuất khỏi hệ thống, xóa bỏ trạng thái đăng nhập khỏi trình duyệt.

Các bước thực hiện:

  1. Tạo một nút hoặc liên kết "Đăng xuất" ở nơi dễ tìm khi người dùng đã đăng nhập (ví dụ: header, menu profile).
  2. Khi nút/liên kết này được nhấn:
    • Xóa token/session ID đã lưu trong trình duyệt (localStorage.removeItem, sessionStorage.removeItem, hoặc xóa cookie).
    • (Tùy chọn nhưng nên làm) Gửi yêu cầu POST hoặc GET đến API back-end để thông báo rằng session trên server cũng cần được hủy (nếu bạn đang dùng session-based auth hoặc cần invalidate token).
    • Cập nhật trạng thái đăng nhập trong ứng dụng front-end (đặt isLoggedIn về false).
    • Chuyển hướng người dùng về trang chủ hoặc trang đăng nhập.

Code minh họa (JavaScript):

function handleLogout() {
    // Xóa token từ localStorage
    localStorage.removeItem('authToken');
    console.log("Token đã được xóa khỏi localStorage.");

    // TODO: Gửi yêu cầu đến API back-end để invalid token/session trên server (nếu cần)
    // try {
    //     await fetch('YOUR_BACKEND_LOGOUT_API_URL', { method: 'POST' });
    //     console.log('Đăng xuất trên server thành công.');
    // } catch (error) {
    //     console.error('Lỗi khi gọi API logout server:', error);
    //     // Có thể bỏ qua lỗi này nếu việc xóa token client là đủ cho mục đích của bạn
    // }


    // Cập nhật trạng thái đăng nhập trong ứng dụng front-end (cần tích hợp với state management)
    // TODO: Update UI state (e.g., hide profile link, show login/register buttons)

    // Chuyển hướng người dùng về trang chủ hoặc trang đăng nhập
    alert('Bạn đã đăng xuất thành công.');
    window.location.href = '/'; // Chuyển về trang chủ
    // Hoặc: window.location.href = '/login'; // Chuyển về trang đăng nhập
}

// Gắn hàm này vào sự kiện click của nút Đăng xuất
// Ví dụ: const logoutButton = document.getElementById('logout-button');
// logoutButton.addEventListener('click', handleLogout);
  • Giải thích: Hàm handleLogout thực hiện các bước cần thiết để kết thúc phiên làm việc của người dùng trên phía front-end. Bước quan trọng nhất là xóa token đã lưu. Việc gọi API logout trên server là tốt nhất để đảm bảo tính toàn vẹn và bảo mật, nhưng với mục đích thực hành front-end đơn giản, việc xóa token client-side và cập nhật UI là đủ để mô phỏng. Cuối cùng, chuyển hướng người dùng giúp đảm bảo họ không còn truy cập vào các trang yêu cầu đăng nhập một cách dễ dàng.
Bài Tập 7: Xử lý Lỗi và Feedback cho Người dùng

Mục tiêu: Hiển thị thông báo lỗi rõ ràng và thân thiện cho người dùng khi có vấn đề xảy ra trong quá trình đăng ký, đăng nhập hoặc truy cập tài nguyên cần xác thực.

Các bước thực hiện:

  1. Xác định các trường hợp lỗi có thể xảy ra (sai thông tin đăng nhập, mật khẩu yếu, email đã tồn tại, token hết hạn, lỗi mạng, lỗi server...).
  2. Trong các hàm xử lý API (Bài 3, Bài 4, Bài 5), bắt và xử lý các loại lỗi khác nhau.
  3. Sử dụng một phần tử HTML (ví dụ: <p>, <div>) để hiển thị thông báo lỗi.
  4. Cập nhật nội dung của phần tử này với thông báo lỗi nhận được từ back-end (nếu có) hoặc một thông báo chung thân thiện.
  5. (Nâng cao) Sử dụng các thư viện thông báo (toasts, modals) để hiển thị lỗi một cách chuyên nghiệp hơn.
  6. (Nâng cao) Xử lý lỗi validation ngay trên client-side trước khi gửi request để giảm tải cho server và phản hồi nhanh hơn cho người dùng.

Code minh họa (Thêm xử lý lỗi vào ví dụ Đăng nhập):

const loginForm = document.getElementById('loginForm');
const errorMessageElement = document.getElementById('errorMessage'); // Thêm 1 element để hiển thị lỗi

loginForm.addEventListener('submit', async (event) => {
    event.preventDefault();

    // Xóa thông báo lỗi cũ
    errorMessageElement.textContent = '';

    const username = document.getElementById('login-username').value;
    const password = document.getElementById('login-password').value;

    const loginData = { username, password };
    const apiUrl = 'YOUR_BACKEND_LOGIN_API_URL';

    try {
        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(loginData),
        });

        const result = await response.json();

        if (response.ok) {
            console.log('Đăng nhập thành công:', result);
            const token = result.token;
            if (token) {
                localStorage.setItem('authToken', token);
                alert('Đăng nhập thành công!'); // Có thể thay bằng thông báo đẹp hơn
                // window.location.href = '/dashboard';
            } else {
                 // Xử lý trường hợp server trả 200 OK nhưng thiếu token
                 errorMessageElement.textContent = 'Lỗi server: Không nhận được token.';
                 console.error('Đăng nhập thành công nhưng không nhận được token.');
            }
        } else {
            console.error('Đăng nhập thất bại:', result);
            // Hiển thị thông báo lỗi từ server hoặc thông báo mặc định
            const errorMessage = result.message || 'Sai tên đăng nhập hoặc mật khẩu.';
            errorMessageElement.textContent = 'Lỗi: ' + errorMessage; // Hiển thị lỗi trên UI
        }

    } catch (error) {
        console.error('Lỗi mạng hoặc hệ thống:', error);
        errorMessageElement.textContent = 'Đã xảy ra lỗi khi kết nối đến máy chủ.'; // Thông báo lỗi kết nối
    }
});
  • Giải thích: Đoạn code này thêm vào việc quản lý một phần tử errorMessageElement trên trang HTML. Trước mỗi lần submit, thông báo lỗi cũ sẽ được xóa. Khi nhận được phản hồi lỗi từ server (!response.ok) hoặc gặp lỗi mạng (catch), chúng ta lấy thông báo lỗi từ phản hồi của server (nếu có) hoặc hiển thị một thông báo mặc định, sau đó gán nội dung này vào errorMessageElement để người dùng nhìn thấy. Đây là cách cơ bản để cung cấp feedback cho người dùng khi có sự cố.

Comments

There are no comments at the moment.