0

PHP và MySQL Bài 30: Xây dựng RESTful API bằng PHP - Trả về JSON thay vì HTML

Trong mô hình MVC ở các bài trước, PHP đóng vai trò "bao thầu" tất cả: vừa truy vấn dữ liệu (Model), vừa xử lý logic (Controller), vừa tự tay vẽ ra giao diện (View - HTML).

Tuy nhiên, trong các hệ thống hiện đại, Frontend (React, Vue, Angular) hoặc Mobile App (iOS, Android) sẽ tự lo phần giao diện. Nhiệm vụ của Backend PHP lúc này là cung cấp dữ liệu thô. Để hai hệ thống khác biệt có thể nói chuyện được với nhau, chúng ta sử dụng API và định dạng JSON.


1. RESTful API là gì?

  • API (Application Programming Interface): Là một giao diện lập trình, đóng vai trò như một người bồi bàn. Người dùng (Client) đưa yêu cầu (Request), API mang yêu cầu đó xuống bếp (Database), lấy món ăn (Dữ liệu) và trả về cho người dùng.
  • RESTful: Là một bộ tiêu chuẩn thiết kế API. Trong RESTful, mỗi URL đại diện cho một "Tài nguyên" (Resource) và chúng ta sử dụng các HTTP Methods để ra lệnh thực hiện các hành động CRUD:
    • GET /api/machines: Đọc danh sách máy bán vé (Read)
    • POST /api/machines: Thêm máy mới (Create)
    • PUT /api/machines/101: Cập nhật toàn bộ máy số 101 (Update)
    • DELETE /api/machines/101: Xóa máy số 101 (Delete)

2. JSON - Ngôn ngữ giao tiếp chung của thế giới web

Thay vì trả về HTML (<div>, <h1>), API sẽ trả về dữ liệu dưới định dạng JSON (JavaScript Object Notation). Đây là một định dạng văn bản siêu nhẹ, dễ đọc đối với cả con người và mọi ngôn ngữ lập trình.

Trong PHP, chúng ta có 2 hàm cốt lõi để làm việc với JSON:

  • json_encode($array): Biến một mảng PHP thành chuỗi JSON (Dùng khi xuất dữ liệu trả về cho Client).
  • json_decode($json, true): Biến một chuỗi JSON thành mảng PHP (Dùng khi nhận dữ liệu từ Client gửi lên).

3. Thực hành: Chuyển đổi Controller truyền thống thành API Endpoint

Giả sử chúng ta muốn xây dựng một API cung cấp danh sách máy bán vé (TVM) đang hoạt động cho một ứng dụng Mobile của nhân viên trạm.

Cách làm cũ (Trả về HTML):

public function index() {
    $machines = $this->machineModel->getAll();
    require 'views/list_machines.php'; // Trả về giao diện
}

Cách làm mới (RESTful API trả về JSON):

Tại file api/machines.php (hoặc trong một Controller dành riêng cho API):

<?php
    // 1. Khai báo các Header cực kỳ quan trọng cho API
    // Cho phép các domain khác (Frontend) gọi API này (CORS)
    header("Access-Control-Allow-Origin: *"); 
    // Khẳng định dữ liệu trả về là định dạng JSON, có hỗ trợ tiếng Việt
    header("Content-Type: application/json; charset=UTF-8");
    // Chỉ cho phép phương thức GET
    header("Access-Control-Allow-Methods: GET");

    require_once '../config/database.php';
    require_once '../models/Machine.php';

    $machineModel = new Machine($pdo);

    // 2. Lấy dữ liệu từ Database
    $machines = $machineModel->getAllActive();

    // 3. Kiểm tra và định hình cấu trúc dữ liệu trả về (Response Payload)
    if (count($machines) > 0) {
        // Tạo một mảng phản hồi chuẩn mực
        $response = [
            "status" => "success",
            "count" => count($machines),
            "data" => $machines
        ];
        
        // Thiết lập mã trạng thái HTTP 200 OK
        http_response_code(200);
        
        // Ép mảng PHP thành JSON và in ra
        echo json_encode($response);
    } else {
        // Thiết lập mã trạng thái HTTP 404 Not Found
        http_response_code(404);
        
        echo json_encode([
            "status" => "error",
            "message" => "Không tìm thấy thiết bị nào đang hoạt động."
        ]);
    }
?>

4. Bí mật của HTTP POST trong API (php://input)

Khi xây dựng API để thêm mới dữ liệu (POST), Frontend (React/Vue) thường gửi dữ liệu lên dưới dạng một cục chuỗi JSON thay vì định dạng form thông thường.

Lúc này, một lỗi kinh điển của các lập trình viên mới làm API là sử dụng $_POST để hứng dữ liệu.

⚠️ Lưu ý: Biến $_POST của PHP sẽ trống rỗng nếu dữ liệu được gửi lên bằng định dạng application/json. Để hứng được cục JSON đó, bạn bắt buộc phải đọc luồng dữ liệu thô đầu vào bằng hàm file_get_contents("php://input").

Ví dụ xử lý POST API (Thêm máy mới):

<?php
    header("Access-Control-Allow-Origin: *");
    header("Content-Type: application/json; charset=UTF-8");
    header("Access-Control-Allow-Methods: POST");

    // Lọc chỉ cho phép POST request
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405); // Method Not Allowed
        echo json_encode(["message" => "Chỉ chấp nhận phương thức POST."]);
        exit();
    }

    // 1. Đọc cục dữ liệu JSON thô gửi từ Client
    $jsonTho = file_get_contents("php://input");

    // 2. Dịch JSON thành mảng PHP (tham số true là bắt buộc để ra mảng)
    $data = json_decode($jsonTho, true);

    // 3. Kiểm tra dữ liệu đầu vào
    if (!empty($data['machine_code']) && !empty($data['station_id'])) {
        
        // Gọi Model để lưu vào DB (Giả lập)
        // $machineModel->create($data['machine_code'], $data['station_id']);
        
        http_response_code(201); // 201 Created
        echo json_encode([
            "status" => "success",
            "message" => "Đã thêm thiết bị mới thành công."
        ]);
    } else {
        http_response_code(400); // 400 Bad Request
        echo json_encode([
            "status" => "error",
            "message" => "Dữ liệu không hợp lệ. Thiếu machine_code hoặc station_id."
        ]);
    }
?>

Tổng kết

Chuyển dịch từ việc render HTML sang viết RESTful API là bước chuyển mình lớn nhất để một Backend Developer có thể làm việc trong các hệ thống Microservices, tham gia vào các team có Frontend/Backend tách biệt, hoặc phục vụ cho ứng dụng Mobile.

Bạn đã nắm được cách thiết lập HTTP Headers, mã phản hồi (HTTP Status Codes), cách xuất JSON (json_encode) và cách hứng JSON (php://input). Để kiểm thử các API này mà không cần viết code Frontend, các lập trình viên trên toàn thế giới sử dụng một công cụ đắc lực tên là Postman.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí