REST (Representational State Transfer) 是網路應用最主流的架構風格。本文示範使用原生 PHP (無框架) 從零建構完整的 RESTful Web Service,涵蓋 URI 設計、HTTP 方法對應、狀態碼、內容協商 (JSON/XML/HTML)、路由重寫、錯誤處理與測試驗證。
| 原則 | 說明 |
| 資源導向 | URI 識別資源,非動作 (如 /users/123 而非 /getUser?id=123) |
| 統一介面 | HTTP 方法對應 CRUD:GET(讀)、POST(建)、PUT/PATCH(更新)、DELETE(刪) |
| 無狀態 | 每個請求包含完整資訊,伺服器不保存客戶端狀態 |
| 可快取 | 回應標示可快取性 (ETag, Cache-Control) |
| 分層系統 | 客戶端不需知曉中間代理、負載平衡器 |
專案結構規劃
rest-example/
├── .htaccess # Apache 路由重寫
├── RestController.php # 請求分派器
├── SimpleRest.php # 基礎回應處理類 (HTTP 狀態碼、內容協商)
├── SiteRestHandler.php # 業務邏輯處理類
└── Site.php # 資料模型類
核心程式碼實作
1. 資料模型
<?php
// Site.php
class Site {
private $sites = [
1 => 'TaoBao', 2 => 'Google', 3 => 'Runoob',
4 => 'Baidu', 5 => 'Weibo', 6 => 'Sina'
];
public function getAllSite() { return $this->sites; }
public function getSite($id) {
return isset($this->sites[$id]) ? [$id => $this->sites[$id]] : [];
}
}
2. 基礎回應類:HTTP 狀態碼與內容協商
<?php
// SimpleRest.php
class SimpleRest {
private $httpVersion = "HTTP/1.1";
public function setHttpHeaders($contentType, $statusCode) {
$statusMessage = $this->getHttpStatusMessage($statusCode);
header($this->httpVersion . " " . $statusCode . " " . $statusMessage);
header("Content-Type: " . $contentType);
}
private function getHttpStatusMessage($statusCode) {
$httpStatus = [
200 => 'OK', 201 => 'Created', 204 => 'No Content',
400 => 'Bad Request', 401 => 'Unauthorized', 404 => 'Not Found',
500 => 'Internal Server Error', 503 => 'Service Unavailable',
// ... 完整狀態碼表
];
return $httpStatus[$statusCode] ?? $httpStatus[500];
}
protected function encodeJson($data) { return json_encode($data); }
protected function encodeXml($data) {
$xml = new SimpleXMLElement('<?xml version="1.0"?><site/>');
foreach ($data as $k => $v) $xml->addChild($k, $v);
return $xml->asXML();
}
protected function encodeHtml($data) {
$html = "<table border='1'>";
foreach ($data as $k => $v) $html .= "<tr><td>$k</td><td>$v</td></tr>";
return $html . "</table>";
}
}
3. 業務處理類:繼承基礎類,實作資源操作
<?php
// SiteRestHandler.php
require_once("SimpleRest.php");
require_once("Site.php");
class SiteRestHandler extends SimpleRest {
public function getAllSites() {
$site = new Site();
$rawData = $site->getAllSite();
$this->respond($rawData);
}
public function getSite($id) {
$site = new Site();
$rawData = $site->getSite($id);
$this->respond($rawData);
}
private function respond($rawData) {
$statusCode = empty($rawData) ? 404 : 200;
if (empty($rawData)) $rawData = ['error' => 'No sites found!'];
$contentType = $_SERVER['HTTP_ACCEPT'] ?? 'application/json';
$this->setHttpHeaders($contentType, $statusCode);
if (strpos($contentType, 'application/json') !== false) {
echo $this->encodeJson($rawData);
} elseif (strpos($contentType, 'text/html') !== false) {
echo $this->encodeHtml($rawData);
} elseif (strpos($contentType, 'application/xml') !== false) {
echo $this->encodeXml($rawData);
}
}
}
4. 控制器:路由分派
<?php
// RestController.php
require_once("SiteRestHandler.php");
$view = $_GET['view'] ?? '';
$handler = new SiteRestHandler();
switch ($view) {
case 'all':
$handler->getAllSites();
break;
case 'single':
$handler->getSite($_GET['id'] ?? '');
break;
default:
header("HTTP/1.1 404 Not Found");
echo json_encode(['error' => 'Not Found']);
}
5. Apache 路由重寫
# .htaccess
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^site/list/$ RestController.php?view=all [NC,QSA]
RewriteRule ^site/list/([0-9]+)/$ RestController.php?view=single&id=$1 [NC,QSA]
API 端點與測試
| 資源 | HTTP 方法 | URI | 說明 |
| 站點列表 | GET | /site/list/ | 取得所有站點 |
| 單一站點 | GET | /site/list/{id}/ | 取得指定 ID 站點 |
使用 curl 測試
# 取得所有站點 (JSON)
curl -H "Accept: application/json" https://localhost/restexample/site/list/
# 取得 ID=3 站點 (XML)
curl -H "Accept: application/xml" https://localhost/restexample/site/list/3/
# 瀏覽器直接訪問 (HTML)
# https://localhost/restexample/site/list/
進階擴充:完整 CRUD 實作
// SiteRestHandler.php 擴充
public function createSite($data) {
// 驗證 $data
$newId = max(array_keys($this->sites)) + 1;
$this->sites[$newId] = $data['name'];
$this->respond(['id' => $newId, 'name' => $data['name']], 201);
}
public function updateSite($id, $data) {
if (!isset($this->sites[$id])) $this->respond(['error' => 'Not found'], 404);
$this->sites[$id] = $data['name'];
$this->respond(['id' => $id, 'name' => $data['name']]);
}
public function deleteSite($id) {
if (!isset($this->sites[$id])) $this->respond(['error' => 'Not found'], 404);
unset($this->sites[$id]);
$this->respond([], 204); // No Content
}
控制器擴充
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pathParts = explode('/', trim($path, '/'));
if ($pathParts[0] === 'site' && $pathParts[1] === 'list') {
$id = $pathParts[2] ?? null;
switch ($method) {
case 'GET':
$id ? $handler->getSite($id) : $handler->getAllSites();
break;
case 'POST':
$input = json_decode(file_get_contents('php://input'), true);
$handler->createSite($input);
break;
case 'PUT':
case 'PATCH':
$input = json_decode(file_get_contents('php://input'), true);
$handler->updateSite($id, $input);
break;
case 'DELETE':
$handler->deleteSite($id);
break;
default:
header("HTTP/1.1 405 Method Not Allowed");
}
}
安全性與最佳實務
- 輸入驗證:所有輸入嚴格驗證、清理
- 認證授權:JWT、API Key、OAuth2 整合
- 速率限制:防止濫用 (Token Bucket、Leaky Bucket)
- HTTPS 強制:生產環境強制 TLS
- 錯誤資訊最小化:生產環境隱藏堆疊追蹤
- 版本控制:URI 包含版本
/v1/site/list/ 或 Header 版本協商
- API 文件:OpenAPI/Swagger 自動生成
總結
使用原生 PHP 建構 RESTful API 能深入理解 HTTP 語意、路由分派、內容協商等核心機制。生產環境建議採用成熟框架 (Laravel, Slim, Symfony) 獲得路由、中介軟體、驗證、ORM 等開箱即用功能,但理解底層實作原理對除錯、效能調優、架構決策至關重要。
- 上一篇文章: PHP PCRE 正則表達式完全指南:函數、修飾符、模式與實戰技巧
- 下一篇文章: PHP GD 圖像處理完全指南:安裝配置、核心函數與實戰應用
Copyright ©
程式碼之家 All Rights Reserved