一、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服务,为现代应用开发提供强大的后端支持。

