首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP cURL 打造跨平台 RESTful API:伺服器端與客戶端完整實現

     編輯:PHP 相關知識

架構概覽

  • 傳輸層:HTTP/HTTPS (cURL)
  • 協定:RESTful (資源導向、狀態無關)
  • 資料格式:JSON
  • 認證:Token + 簽名驗證 (Client_Token + Client_Code)
  • 路由模式:PATH_INFO (/resource/id)

核心路由映射

HTTP 方法PATH_INFO語意對應方法
GET/users列出所有使用者 (分頁)_get()
POST/users建立新使用者_post()
GET/users/123取得 ID=123 使用者詳情_get()
PUT/users/123更新 ID=123 使用者_put()
DELETE/users/123刪除 ID=123 使用者_delete()

伺服器端:ApiServer 基礎類別

<?php
abstract class ApiServer {
    private string $method = '';
    protected array $params = [];
    protected string $resource = '';
    protected string $resourceId = '';

    public function __construct() {
        $this->authorize();           // 驗證簽名
        $this->method = strtolower($_SERVER['REQUEST_METHOD']);
        $this->parsePathInfo();       // 解析資源與 ID
        $this->collectParams();       // 收集參數 (GET/POST/PUT body)
        $this->dispatch();            // 分發處理
    }

    private function authorize(): void {
        $token = $_SERVER['HTTP_CLIENT_TOKEN'] ?? '';
        $code  = $_SERVER['HTTP_CLIENT_CODE'] ?? '';
        $expected = md5(substr(md5($token), 8, 24) . $token);
        if ($expected !== $code) {
            $this->output(1); // 驗證失敗
            exit;
        }
    }

    private function parsePathInfo(): void {
        $path = $_SERVER['PATH_INFO'] ?? '/';
        $parts = array_filter(explode('/', trim($path, '/')));
        $this->resource = $parts[0] ?? '';
        $this->resourceId = $parts[1] ?? '';
    }

    private function collectParams(): void {
        switch ($this->method) {
            case 'get':
            case 'delete':
                $this->params = $_GET;
                break;
            case 'post':
                $this->params = $_POST;
                break;
            case 'put':
                parse_str(file_get_contents('php://input'), $this->params);
                break;
        }
    }

    private function dispatch(): void {
        $handler = '_' . $this->method;
        if (method_exists($this, $handler)) {
            $this->$handler();
        } else {
            $this->output(2, ['error' => 'Method Not Allowed']);
        }
    }

    // 子類別必須實現
    abstract protected function _get(): void;
    abstract protected function _post(): void;
    abstract protected function _put(): void;
    abstract protected function _delete(): void;

    protected function output(int $status, array $data = []): void {
        $messages = [
            0 => ['code' => 1, 'info' => '成功', 'data' => $data],
            1 => ['code' => 0, 'info' => '驗證失敗'],
            2 => ['code' => 0, 'info' => '不支援的方法'],
        ];
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($messages[$status] ?? ['code' => 0, 'info' => '未知狀態']);
    }
}

客戶端:RestClient 封裝

<?php
class RestClient {
    private string $url;
    private string $method;
    private array $data;
    private resource $ch;
    private array $headers = [];
    private const TOKEN = 'your_secret_token';

    public function __construct(string $url, array $data = [], string $method = 'get') {
        $this->url = $url;
        $this->method = strtolower($method);
        $this->data = $data;

        // GET 參數附加到 URL
        if ($this->method === 'get' && !empty($data)) {
            $this->url .= '?' . http_build_query($data);
        }

        $this->ch = curl_init($this->url);
        curl_setopt_array($this->ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => $this->buildHeaders(),
        ]);
    }

    private function buildHeaders(): array {
        $headers = ['Content-Type: application/json'];
        if (self::TOKEN) {
            $code = md5(substr(md5(self::TOKEN), 8, 24) . self::TOKEN);
            $headers[] = 'Client_Token: ' . self::TOKEN;
            $headers[] = 'Client_Code: ' . $code;
        }
        return $headers;
    }

    public function execute(): string|false {
        $opts = [
            'post'   => [CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($this->data)],
            'put'    => [CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => json_encode($this->data)],
            'delete' => [CURLOPT_CUSTOMREQUEST => 'DELETE'],
            'get'    => [CURLOPT_HTTPGET => true],
        ];
        curl_setopt_array($this->ch, $opts[$this->method] ?? $opts['get']);
        return curl_exec($this->ch);
    }

    public function getInfo(): array {
        return curl_getinfo($this->ch);
    }

    public function __destruct() {
        if ($this->ch) curl_close($this->ch);
    }
}

使用範例

測試伺服器

<?php
// testServer.php
require 'ApiServer.php';

class TestServer extends ApiServer {
    protected function _get(): void {
        $this->output(0, ['method' => 'GET', 'resource' => $this->resource, 'id' => $this->resourceId]);
    }
    protected function _post(): void { $this->output(0, ['method' => 'POST', 'data' => $this->params]); }
    protected function _put(): void  { $this->output(0, ['method' => 'PUT', 'id' => $this->resourceId, 'data' => $this->params]); }
    protected function _delete(): void { $this->output(0, ['method' => 'DELETE', 'id' => $this->resourceId]); }
}

new TestServer();

測試客戶端

<?php
// testClient.php
require 'RestClient.php';

$client = new RestClient('https://api.example.com/users/123', [], 'get');
$response = $client->execute();
$info = $client->getInfo();

echo "HTTP Code: {$info['http_code']}\n";
echo "Response: $response\n";
/* 輸出範例:
HTTP Code: 200
Response: {"code":1,"info":"成功","data":{"method":"GET","resource":"users","id":"123"}}
*/

部署注意事項

  • PATH_INFO 支援:Nginx 需設定 fastcgi_split_path_info;Apache 需啟用 AcceptPathInfo On。
  • PUT/DELETE 請求體:PHP 不自動解析,需手動 file_get_contents('php://input') + parse_str 或 json_decode。
  • HTTPS:生產環境強制 TLS,cURL 設定 CURLOPT_SSL_VERIFYPEER => true。
  • 錯誤處理:統一 JSON 格式回傳,包含 code、message、data。
  • 速率限制:建議加入 API 節流 (Token Bucket / Leaky Bucket)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved