13. RESTful API 开发

13.RESTful API 开发

API 架构设计

在现代 Web 开发中,RESTful API 已经成为前后端分离架构的标准选择。使用 Phalcon 框架开发 API 时,最简洁高效的方式是采用微应用(Micro Application)模式。这种模式不需要完整的 MVC 结构,可以直接定义路由和处理逻辑,非常适合构建轻量级 API 服务。

Phalcon 微应用的基本结构非常简单,通常包含入口文件、模型定义和路由配置三个部分。以下是一个典型的 API 项目结构:

my-rest-api/
    models/
        Robots.php
    index.php
    .htaccess

入口文件 index.php 是 API 的请求处理中心。首先需要配置 URL 重写规则,通过 .htaccess 文件将所有请求导向入口文件:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L]
</IfModule>

这个配置确保所有非文件请求都会被发送到 index.php 处理,为后续的路由解析做好准备。

在 index.php 中,我们需要初始化 Phalcon 微应用并配置依赖注入。数据库连接是 API 的基础,这里以 MySQL 为例进行配置:

<?php

use Phalcon\Autoload\Loader;
use Phalcon\Mvc\Micro;
use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

// 自动加载配置
$loader = new Loader();
$loader->setNamespaces([
    'MyApp\Models' => __DIR__ . '/models/',
]);
$loader->register();

// 依赖注入容器
$container = new FactoryDefault();

// 数据库连接配置
$container->set('db', function () {
    return new PdoMysql([
        'host'     => 'localhost',
        'username' => 'asimov',
        'password' => 'zeroth',
        'dbname'   => 'robotics',
    ]);
});

// 初始化微应用
$app = new Micro($container);

这段代码完成了三个关键任务:配置自动加载器以加载模型类、设置数据库连接服务、初始化微应用实例。这样就搭建好了 API 的基础架构,接下来可以开始定义具体的业务路由和处理逻辑了。

请求处理

RESTful API 的核心是通过 HTTP 方法对资源进行操作。Phalcon 微应用提供了直观的 HTTP 方法路由定义方式,让我们可以轻松映射不同的请求类型到相应的处理函数。

路由定义

Phalcon 微应用支持直接通过 HTTP 方法名来定义路由,如 get()、post()、put() 和 delete()。以下是一个完整的 API 路由配置示例:

<?php

// 获取所有机器人
$app->get('/api/robots', function () use ($app) {
    // 处理逻辑
});

// 搜索机器人
$app->get('/api/robots/search/{name}', function ($name) use ($app) {
    // 处理逻辑
});

// 获取单个机器人
$app->get('/api/robots/{id:[0-9]+}', function ($id) use ($app) {
    // 处理逻辑
});

// 创建机器人
$app->post('/api/robots', function () use ($app) {
    // 处理逻辑
});

// 更新机器人
$app->put('/api/robots/{id:[0-9]+}', function ($id) use ($app) {
    // 处理逻辑
});

// 删除机器人
$app->delete('/api/robots/{id:[0-9]+}', function ($id) use ($app) {
    // 处理逻辑
});

// 处理请求
$app->handle($_SERVER["REQUEST_URI"]);

路由定义中的 {id:[0-9]+} 是一个带正则表达式约束的参数,表示只能匹配数字。这种方式可以有效过滤非法请求,提高 API 的健壮性。

数据获取与处理

对于 GET 请求,参数通常通过 URL 路径或查询字符串传递;而 POST 和 PUT 请求则通常使用 JSON 格式的请求体。Phalcon 提供了便捷的方法来获取这些数据。

获取 URL 路径参数非常简单,直接在处理函数中声明参数即可:

$app->get('/api/robots/search/{name}', function ($name) use ($app) {
    // $name变量包含URL中的搜索关键词
});

对于 JSON 请求体,Phalcon 的请求对象提供了 getJsonRawBody() 方法,可以直接解析 JSON 数据为 PHP 对象:

$app->post('/api/robots', function () use ($app) {
    $robot = $app->request->getJsonRawBody();
    // $robot是包含请求数据的对象
});

数据库操作

API 的数据操作通常通过模型进行。Phalcon 的模型系统支持 PHQL(Phalcon Query Language),这是一种类 SQL 的查询语言,提供了面向对象的数据库操作方式。

以下是一个使用 PHQL 查询所有机器人的示例:

$app->get('/api/robots', function () use ($app) {
    $phql = 'SELECT id, name FROM MyApp\Models\Robots ORDER BY name';
    $robots = $app->modelsManager->executeQuery($phql);

    $data = [];
    foreach ($robots as $robot) {
        $data[] = [
            'id'   => $robot->id,
            'name' => $robot->name,
        ];
    }

    echo json_encode($data);
});

PHQL 支持参数绑定,可以有效防止 SQL 注入攻击。例如,搜索功能可以这样实现:

$app->get('/api/robots/search/{name}', function ($name) use ($app) {
    $phql = 'SELECT * FROM MyApp\Models\Robots WHERE name LIKE :name: ORDER BY name';
    $robots = $app->modelsManager->executeQuery($phql, [
        'name' => '%' . $name . '%'
    ]);

    $data = [];
    foreach ($robots as $robot) {
        $data[] = [
            'id'   => $robot->id,
            'name' => $robot->name,
        ];
    }

    echo json_encode($data);
});

响应格式化

一致的响应格式对于 API 用户体验至关重要。一个好的 API 应该返回清晰的状态码和结构化的响应数据,以便客户端能够轻松处理成功和错误情况。

状态码使用

HTTP 状态码是表示请求处理结果的标准方式,正确使用状态码可以让客户端快速了解请求状态:

  • 200 OK:请求成功
  • 201 Created:资源创建成功
  • 400 Bad Request:请求参数错误
  • 404 Not Found:资源不存在
  • 409 Conflict:资源冲突(如重复创建)
  • 500 Internal Server Error:服务器内部错误

在 Phalcon 中,可以通过 Response 对象设置状态码:

use Phalcon\Http\Response;

$response = new Response();
$response->setStatusCode(201, 'Created');

响应数据格式

API 响应应该使用一致的 JSON 格式。成功响应通常包含状态标识和数据,错误响应则包含状态标识和错误信息数组。

以下是一个成功响应的示例:

$response->setJsonContent([
    'status' => 'OK',
    'data'   => $robot
]);

错误响应示例:

$response->setJsonContent([
    'status'   => 'ERROR',
    'messages' => $errors
]);

完整的响应处理示例

结合状态码和响应格式,以下是一个创建资源的完整处理函数:

$app->post('/api/robots', function () use ($app) {
    $robot = $app->request->getJsonRawBody();
    $phql = 'INSERT INTO MyApp\Models\Robots (name, type, year) VALUES (:name:, :type:, :year:)';

    $status = $app->modelsManager->executeQuery($phql, [
        'name' => $robot->name,
        'type' => $robot->type,
        'year' => $robot->year,
    ]);

    $response = new Response();

    if ($status->success() === true) {
        $response->setStatusCode(201, 'Created');
        $robot->id = $status->getModel()->id;
        $response->setJsonContent([
            'status' => 'OK',
            'data'   => $robot
        ]);
    } else {
        $response->setStatusCode(409, 'Conflict');
        $errors = [];
        foreach ($status->getMessages() as $message) {
            $errors[] = $message->getMessage();
        }
        $response->setJsonContent([
            'status'   => 'ERROR',
            'messages' => $errors
        ]);
    }

    return $response;
});

这个示例中,当资源创建成功时返回 201 状态码和包含新创建资源数据的响应;当发生冲突(如重复名称)时返回 409 状态码和错误信息数组。

身份验证与授权

API 的安全性是生产环境中必须考虑的重要因素。JSON Web Token(JWT)是一种流行的 API 身份验证方案,Phalcon 提供了专门的 JWT 组件来简化实现。

JWT 基础

JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部指定加密算法,载荷包含用户信息和过期时间等声明,签名用于验证令牌的完整性。

Phalcon 的 JWT 组件主要包含三个类:

  • Builder:用于创建 JWT 令牌
  • Parser:用于解析接收到的 JWT 令牌
  • Validator:用于验证 JWT 令牌的有效性

生成 JWT 令牌

以下是使用 Phalcon 生成 JWT 令牌的示例代码:

<?php

use Phalcon\Encryption\Security\JWT\Builder;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;

// 创建HMAC签名器,默认使用sha512算法
$signer = new Hmac();

// 创建JWT构建器
$builder = new Builder($signer);

$now = new DateTimeImmutable();
$passphrase = 'your-secure-passphrase';

// 设置令牌声明
$builder
    ->setAudience('https://your-api.com')      // 受众
    ->setExpirationTime($now->modify('+1 day')->getTimestamp()) // 过期时间
    ->setIssuedAt($now->getTimestamp())        // 签发时间
    ->setIssuer('https://your-api.com')        // 签发者
    ->setNotBefore($now->modify('-1 minute')->getTimestamp()) // 生效时间
    ->setSubject('user-auth')                  // 主题
    ->setPassphrase($passphrase);              // 签名密钥

// 生成令牌对象
$tokenObject = $builder->getToken();

// 获取令牌字符串
$token = $tokenObject->getToken();

生成的令牌可以返回给客户端,客户端在后续请求中通过 Authorization 头携带此令牌:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiIsImN0eSI6ImFwcGxpY2F0aW9uXC9qc29uIn0...

验证 JWT 令牌

服务器端接收到令牌后,需要进行验证以确保其有效性。以下是验证 JWT 令牌的示例代码:

<?php

use Phalcon\Encryption\Security\JWT\Token\Parser;
use Phalcon\Encryption\Security\JWT\Validator;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;

// 从请求头获取令牌
$tokenReceived = $app->request->getHeader('Authorization');
$tokenReceived = str_replace('Bearer ', '', $tokenReceived);

// 创建签名器和解析器
$signer = new Hmac();
$parser = new Parser();

// 解析令牌
$tokenObject = $parser->parse($tokenReceived);

// 创建验证器,允许100秒的时间偏差
$validator = new Validator($tokenObject, 100);

$passphrase = 'your-secure-passphrase';
$audience = 'https://your-api.com';
$issuer = 'https://your-api.com';

// 执行验证
$validator
    ->validateAudience($audience)
    ->validateIssuer($issuer)
    ->validateSignature($signer, $passphrase);

// 检查验证结果
if (count($validator->getErrors()) > 0) {
    // 验证失败,返回401 Unauthorized
    $response->setStatusCode(401, 'Unauthorized');
    $response->setJsonContent([
        'status' => 'ERROR',
        'message' => 'Invalid token'
    ]);
    return $response;
}

集成到 API 中

为了保护 API 资源,可以创建一个验证中间件,在处理实际请求前验证 JWT 令牌:

<?php

function authMiddleware($app) {
    return function () use ($app) {
        // 令牌验证逻辑
        // ...
        if (验证失败) {
            $response = new Response();
            $response->setStatusCode(401, 'Unauthorized');
            $response->setJsonContent([
                'status' => 'ERROR',
                'message' => 'Authentication required'
            ]);
            $response->send();
            exit;
        }
    };
}

// 使用中间件保护路由
$app->get('/api/robots', function () use ($app) {
    // 处理逻辑
})->before(authMiddleware($app));

API 文档生成

良好的 API 文档是 API 易用性的关键。虽然 Phalcon 没有内置的 API 文档生成工具,但可以通过代码注释和第三方工具来生成专业的 API 文档。

使用注释生成文档

可以使用类似 Swagger 的注释规范来标记 API 接口,然后通过工具生成文档。例如:

/**
 * 获取所有机器人
 *
 * @api {get} /api/robots 获取机器人列表
 * @apiName GetRobots
 * @apiGroup Robots
 *
 * @apiSuccess {Object[]} data 机器人列表
 * @apiSuccess {Number} data.id 机器人ID
 * @apiSuccess {String} data.name 机器人名称
 */
$app->get('/api/robots', function () use ($app) {
    // 处理逻辑
});

使用 Phalcon 注解组件

Phalcon 的注解(Annotations)组件可以解析代码中的注释,提取 API 信息。以下是一个使用注解的示例:

use Phalcon\Annotations\Adapter\Memory as MemoryAdapter;

$reader = new MemoryAdapter();
$reflector = $reader->get('MyApp\Controllers\RobotController');

// 获取类注解
$classAnnotations = $reflector->getClassAnnotations();

// 获取方法注解
$methodAnnotations = $reflector->getMethod('index')->getAnnotations();

通过解析这些注解,可以构建自定义的 API 文档生成器,或者集成到现有的文档系统中。

第三方文档工具

对于生产环境,建议使用成熟的 API 文档工具,如 Swagger UI、Postman 或 ApiDoc 等。这些工具通常提供直观的界面和交互功能,方便 API 用户测试和使用接口。

以 ApiDoc 为例,首先需要安装 ApiDoc:

npm install apidoc -g

然后在项目中创建配置文件 apidoc.json,并在 API 代码中添加注释:

/**
 * @api {
 *   get
 * } /api/robots 获取机器人列表
 * @apiVersion 1.0.0
 * @apiName GetRobots
 * @apiGroup Robot
 *
 * @apiSuccess {
 *   array
 * } data 机器人数组
 */

最后运行命令生成文档:

apidoc -i src/ -o public/apidoc/

这将在 public/apidoc 目录下生成静态 HTML 文档,可以直接通过浏览器访问。

实践案例

为了更好地理解 Phalcon REST API 的开发流程,我们来构建一个完整的机器人管理 API 示例。

数据库设计

首先创建数据库表结构:

CREATE DATABASE `robotics`;

CREATE TABLE `robotics`.`robots` (
 `id`    int(10)      unsigned         not null auto_increment,
 `name`  varchar(200) collate utf8_bin not null,
 `type`  varchar(20)  collate utf8_bin not null,
 `year`  smallint(4)  unsigned         not null,
 PRIMARY KEY (`id`)
);

模型定义

创建 models/Robots.php 模型文件,包含数据验证逻辑:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Messages\Message;
use Phalcon\Filter\Validation;
use Phalcon\Filter\Validation\Validator\Uniqueness;
use Phalcon\Filter\Validation\Validator\InclusionIn;

class Robots extends Model
{
    public function validation()
    {
        $validator = new Validation();

        $validator->add(
            "type",
            new InclusionIn([
                'message' => 'Type must be "droid", "mechanical", or "virtual"',
                'domain'  => ['droid', 'mechanical', 'virtual'],
            ])
        );

        $validator->add(
            'name',
            new Uniqueness([
                'field'   => 'name',
                'message' => 'The robot name must be unique',
            ])
        );

        if ($this->year < 0) {
            $this->appendMessage(new Message('The year cannot be less than zero'));
        }

        return $this->validate($validator);
    }
}

完整 API 实现

将前面介绍的路由和处理逻辑整合到 index.php 中,实现完整的 CRUD 功能。

测试 API

使用 curl 命令测试 API 功能:

获取所有机器人:

curl -i -X GET http://localhost:8000/api/robots

创建机器人:

curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}' http://localhost:8000/api/robots

更新机器人:

curl -i -X PUT -d '{"name":"R2-D2","type":"droid","year":1977}' http://localhost:8000/api/robots/1

删除机器人:

curl -i -X DELETE http://localhost:8000/api/robots/1

通过这些测试,可以验证 API 的功能是否正常工作。

总结

本章介绍了使用 Phalcon 框架开发 RESTful API 的完整流程,包括架构设计、请求处理、响应格式化、身份验证与授权以及 API 文档生成。通过 Phalcon 的微应用模式和丰富的组件,可以快速构建高性能、安全可靠的 API 服务。

在实际开发中,还需要考虑更多细节,如请求限流、日志记录、错误监控等。但掌握了本章介绍的基础知识,就可以应对大多数 API 开发场景。Phalcon 的 API 开发方式简洁高效,让开发者能够专注于业务逻辑而不是框架细节,这正是 Phalcon 作为高性能框架的优势所在。