一、RESTful API概述与设计原则
RESTful API是一种基于REST架构风格的Web服务接口,它使用HTTP协议定义了一组约束和原则,使Web服务更加简洁、可扩展和易于维护。
RESTful API核心原则:
- 无状态性:每个请求包含所有必要信息,服务器不存储客户端状态
- 统一接口:使用标准HTTP方法(GET、POST、PUT、DELETE等)
- 资源导向:所有内容都是资源,通过URI标识
- 可缓存性:响应应明确表明是否可缓存
- 分层系统:客户端不需要知道是否直接连接到最终服务器
二、项目结构与数据库设计
项目目录结构:
api/ ├── config/ │ └── database.php # 数据库配置 ├── controllers/ │ ├── ApiController.php # 基础控制器 │ └── ProductController.php # 产品控制器 ├── models/ │ ├── Database.php # 数据库连接 │ └── Product.php # 产品模型 ├── utils/ │ ├── Response.php # 响应处理 │ └── JwtAuth.php # JWT认证 └── index.php # 入口文件
数据库设计(products表):
CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, price DECIMAL(10, 2) NOT NULL, category_id INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
三、核心代码实现
1. 数据库配置与连接
创建config/database.php:
<?php class Database { private $host = "localhost"; private $db_name = "api_db"; private $username = "root"; private $password = ""; public $conn; public function getConnection() { $this->conn = null; try { $this->conn = new PDO( "mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password ); $this->conn->exec("set names utf8"); $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $exception) { echo "Connection error: " . $exception->getMessage(); } return $this->conn; } } ?>
2. 响应处理工具
创建utils/Response.php:
<?php class Response { public static function send($data, $status = 200) { http_response_code($status); header('Content-Type: application/json'); echo json_encode([ 'status' => $status, 'data' => $data ]); exit; } public static function error($message, $status = 400) { http_response_code($status); header('Content-Type: application/json'); echo json_encode([ 'status' => $status, 'message' => $message ]); exit; } } ?>
3. 产品模型
创建models/Product.php:
<?php class Product { private $conn; private $table_name = "products"; public $id; public $name; public $description; public $price; public $category_id; public $created_at; public $updated_at; public function __construct($db) { $this->conn = $db; } // 获取所有产品 public function read() { $query = "SELECT * FROM " . $this->table_name . " ORDER BY created_at DESC"; $stmt = $this->conn->prepare($query); $stmt->execute(); return $stmt; } // 获取单个产品 public function readOne() { $query = "SELECT * FROM " . $this->table_name . " WHERE id = ? LIMIT 0,1"; $stmt = $this->conn->prepare($query); $stmt->bindParam(1, $this->id); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); if($row) { $this->name = $row['name']; $this->description = $row['description']; $this->price = $row['price']; $this->category_id = $row['category_id']; $this->created_at = $row['created_at']; $this->updated_at = $row['updated_at']; return true; } return false; } // 创建产品 public function create() { $query = "INSERT INTO " . $this->table_name . " SET name=:name, description=:description, price=:price, category_id=:category_id"; $stmt = $this->conn->prepare($query); // 清理数据 $this->name = htmlspecialchars(strip_tags($this->name)); $this->description = htmlspecialchars(strip_tags($this->description)); $this->price = htmlspecialchars(strip_tags($this->price)); $this->category_id = htmlspecialchars(strip_tags($this->category_id)); // 绑定参数 $stmt->bindParam(":name", $this->name); $stmt->bindParam(":description", $this->description); $stmt->bindParam(":price", $this->price); $stmt->bindParam(":category_id", $this->category_id); if($stmt->execute()) { return true; } return false; } // 更新产品 public function update() { $query = "UPDATE " . $this->table_name . " SET name=:name, description=:description, price=:price, category_id=:category_id WHERE id=:id"; $stmt = $this->conn->prepare($query); // 清理数据 $this->name = htmlspecialchars(strip_tags($this->name)); $this->description = htmlspecialchars(strip_tags($this->description)); $this->price = htmlspecialchars(strip_tags($this->price)); $this->category_id = htmlspecialchars(strip_tags($this->category_id)); $this->id = htmlspecialchars(strip_tags($this->id)); // 绑定参数 $stmt->bindParam(":name", $this->name); $stmt->bindParam(":description", $this->description); $stmt->bindParam(":price", $this->price); $stmt->bindParam(":category_id", $this->category_id); $stmt->bindParam(":id", $this->id); if($stmt->execute()) { return true; } return false; } // 删除产品 public function delete() { $query = "DELETE FROM " . $this->table_name . " WHERE id = ?"; $stmt = $this->conn->prepare($query); $this->id = htmlspecialchars(strip_tags($this->id)); $stmt->bindParam(1, $this->id); if($stmt->execute()) { return true; } return false; } } ?>
4. 产品控制器
创建controllers/ProductController.php:
<?php class ProductController { private $db; private $product; public function __construct($db) { $this->db = $db; $this->product = new Product($db); } // 处理请求 public function handleRequest($method, $id = null) { switch($method) { case 'GET': if($id) { $this->getProduct($id); } else { $this->getAllProducts(); } break; case 'POST': $this->createProduct(); break; case 'PUT': $this->updateProduct($id); break; case 'DELETE': $this->deleteProduct($id); break; default: Response::error('Method not allowed', 405); break; } } // 获取所有产品 private function getAllProducts() { $stmt = $this->product->read(); $num = $stmt->rowCount(); if($num > 0) { $products_arr = array(); $products_arr["records"] = array(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { extract($row); $product_item = array( "id" => $id, "name" => $name, "description" => html_entity_decode($description), "price" => $price, "category_id" => $category_id, "created_at" => $created_at, "updated_at" => $updated_at ); array_push($products_arr["records"], $product_item); } Response::send($products_arr); } else { Response::send(array("message" => "No products found.")); } } // 获取单个产品 private function getProduct($id) { $this->product->id = $id; if($this->product->readOne()) { $product_arr = array( "id" => $this->product->id, "name" => $this->product->name, "description" => html_entity_decode($this->product->description), "price" => $this->product->price, "category_id" => $this->product->category_id, "created_at" => $this->product->created_at, "updated_at" => $this->product->updated_at ); Response::send($product_arr); } else { Response::error("Product not found", 404); } } // 创建产品 private function createProduct() { $data = json_decode(file_get_contents("php://input")); if( !empty($data->name) && !empty($data->price) && !empty($data->category_id) ) { $this->product->name = $data->name; $this->product->description = $data->description ?? ''; $this->product->price = $data->price; $this->product->category_id = $data->category_id; if($this->product->create()) { Response::send(array("message" => "Product created."), 201); } else { Response::error("Unable to create product."); } } else { Response::error("Unable to create product. Data is incomplete."); } } // 更新产品 private function updateProduct($id) { $data = json_decode(file_get_contents("php://input")); $this->product->id = $id; if($this->product->readOne()) { $this->product->name = $data->name ?? $this->product->name; $this->product->description = $data->description ?? $this->product->description; $this->product->price = $data->price ?? $this->product->price; $this->product->category_id = $data->category_id ?? $this->product->category_id; if($this->product->update()) { Response::send(array("message" => "Product updated.")); } else { Response::error("Unable to update product."); } } else { Response::error("Product not found", 404); } } // 删除产品 private function deleteProduct($id) { $this->product->id = $id; if($this->product->readOne()) { if($this->product->delete()) { Response::send(array("message" => "Product deleted.")); } else { Response::error("Unable to delete product."); } } else { Response::error("Product not found", 404); } } } ?>
5. 入口文件与路由
创建index.php:
<?php header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // 引入必要文件 require_once 'config/database.php'; require_once 'models/Product.php'; require_once 'controllers/ProductController.php'; require_once 'utils/Response.php'; // 获取请求方法和参数 $method = $_SERVER['REQUEST_METHOD']; $request_uri = $_SERVER['REQUEST_URI']; $uri_segments = explode('/', trim($request_uri, '/')); // 简单路由解析 $resource = $uri_segments[1] ?? ''; $id = $uri_segments[2] ?? null; // 初始化数据库连接 $database = new Database(); $db = $database->getConnection(); // 路由分发 switch($resource) { case 'products': $controller = new ProductController($db); $controller->handleRequest($method, $id); break; default: Response::error('Resource not found', 404); break; } ?>
四、API测试与使用
1. 获取所有产品(GET)
curl -X GET http://localhost/api/products
2. 获取单个产品(GET)
curl -X GET http://localhost/api/products/1
3. 创建产品(POST)
curl -X POST http://localhost/api/products -H "Content-Type: application/json" -d '{ "name": "iPhone 13", "description": "最新款iPhone", "price": 5999, "category_id": 1 }'
4. 更新产品(PUT)
curl -X PUT http://localhost/api/products/1 -H "Content-Type: application/json" -d '{ "name": "iPhone 13 Pro", "price": 7999 }'
5. 删除产品(DELETE)
curl -X DELETE http://localhost/api/products/1
五、高级功能扩展
1. JWT认证实现
创建utils/JwtAuth.php:
<?php class JwtAuth { private $secret_key = "your_secret_key"; public function generateToken($payload) { $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']); $payload = json_encode($payload); $base64UrlHeader = $this->base64UrlEncode($header); $base64UrlPayload = $this->base64UrlEncode($payload); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $this->secret_key, true); $base64UrlSignature = $this->base64UrlEncode($signature); return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; } public function validateToken($token) { $tokenParts = explode('.', $token); $header = base64_decode($tokenParts[0]); $payload = base64_decode($tokenParts[1]); $signatureProvided = $tokenParts[2]; $base64UrlHeader = $this->base64UrlEncode($header); $base64UrlPayload = $this->base64UrlEncode($payload); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $this->secret_key, true); $base64UrlSignature = $this->base64UrlEncode($signature); return ($base64UrlSignature === $signatureProvided); } private function base64UrlEncode($text) { return str_replace( ['+', '/', '='], ['-', '_', ''], base64_encode($text) ); } } ?>
2. 分页与过滤
在Product模型中添加分页方法:
public function readPaging($from_record_num, $records_per_page) { $query = "SELECT * FROM " . $this->table_name . " ORDER BY created_at DESC LIMIT ?, ?"; $stmt = $this->conn->prepare($query); $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT); $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT); $stmt->execute(); return $stmt; } public function count() { $query = "SELECT COUNT(*) as total_rows FROM " . $this->table_name; $stmt = $this->conn->prepare($query); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row['total_rows']; }
六、部署与优化建议
部署注意事项:
- 使用HTTPS确保数据传输安全
- 配置适当的CORS策略
- 使用环境变量管理敏感信息
- 实施API速率限制防止滥用
- 启用Gzip压缩减少响应大小
性能优化建议:
- 使用OPcache加速PHP执行
- 实施数据库查询缓存
- 使用Redis或Memcached缓存频繁访问的数据
- 对响应数据实施压缩
- 使用CDN分发静态资源
安全性建议:
- 验证和过滤所有输入数据
- 使用预处理语句防止SQL注入
- 实施适当的身份验证和授权机制
- 定期更新依赖库和框架
- 记录和监控API访问日志
七、总结
本教程详细介绍了如何使用PHP构建一个完整的RESTful API,涵盖了从数据库设计、模型创建、控制器实现到路由处理的全过程。通过这个示例,您可以学习到:
- RESTful API的设计原则和最佳实践
- 使用PDO进行安全的数据库操作
- 实现标准的CRUD操作(创建、读取、更新、删除)
- 处理HTTP请求和生成JSON响应
- 扩展API功能(认证、分页、过滤等)
这个API可以作为各种前端应用(Web、移动应用等)的后端服务,提供了清晰的数据接口和完整的业务逻辑处理能力。您可以根据实际需求进一步扩展功能,如用户认证、文件上传、更复杂的查询功能等。
通过掌握这些技能,您将能够构建专业级的Web服务,为现代应用开发提供强大的后端支持。