albertorc87 / easyapi
EasyAPI 是一个针对开发 REST 架构 API 的微型框架,不依赖任何库。
Requires
- php: 7.4 - 8.1
README
EasyAPI 是一个针对开发 REST 架构 API 的微型框架,不依赖任何库。
学习如何使用 APIs 的指南
如果文档中讨论的许多内容您还不清楚,我可以提供这个指南,其中解释了如何使用 APIs 以及如何构建它们。
安装
composer require albertorc87/easyapi
Hello world
在项目的根目录下创建一个名为 public 的目录,并在其中创建一个名为 index.php 的文件,代码如下
<?php require __DIR__ . '/../vendor/autoload.php'; $_ENV['DEBUG_MODE'] = true; $_ENV['ROOT_PROJECT'] = __DIR__ . '/../'; use EasyAPI\Router; Router::get('/hello-world', function() { return view('json', 'Hello world'); }); $app = new EasyAPI\App(); $app->send();
启动 PHP 服务器以查看其运行情况
php -S 0.0.0.0:8011 -t public/
为了使项目正常工作,我们必须创建环境变量 DEBUG_MODE 和 ROOT_PROJECT,以便在创建 EasyAPI\App 实例之前使框架工作。
DEBUG_MODE 如果设置为 true,则未处理的异常将显示在响应中;否则,将在项目的根目录下的 logs 文件夹中保存日志。
ROOT_PROJECT 这里我们需要添加项目的根路径。目前,它仅用于创建 logs 文件夹并保存生成的日志。
而不是像我之前那样在代码中添加,我们可以使用类似 dotenv 的库来创建一个 .env 文件,并让这个库负责加载环境变量。
路由
为了创建路由,我们需要导入 EasyAPI\Router 类,然后添加我们想要的 URL。我们使用 Router 的方法来指定 HTTP 方法,有五个可用方法:get、post、put、patch 和 delete。
<?php use EasyAPI\Router; use App\Controller\UserController; use App\Middleware\IsAuth; use App\Middleware\IsAdmin; Router::get('/users', UserController::class . '@all', IsAdmin::class); Router::post('/users', UserController::class . '@create'); Router::put('/users/(?<id>\d+)', UserController::class . '@update', IsAuth::class); Router::patch('/users/(?<id>\d+)', UserController::class . '@partial', IsAuth::class); Router::delete('/users/(?<id>\d+)', UserController::class . '@delete', [IsAdmin::class, IsAuth::class]);
第一个参数是要指向的 URI。
第二个参数可以是类和要调用的方法,用 @ 分隔。例如
App\Controller\UserController@all
App\Controller\UserController 是类。
all 是类中包含并想要调用的方法。
第三个参数是可选的,我们可以在这里添加中间件,例如检查用户是否已认证(如果需要的话)。我们将在稍后解释其功能。在这种情况下,我们可以发送一个中间件或中间件数组。
路由参数传递
为了传递路由参数,我们必须使用正则表达式,非常重要,必须指定字段的名称才能使其工作
<?php Router::get('/users/(?<id>\d+)', function(int $id) { return view('json', 'Usuario con id ' . $id); }); Router::get('/hello/(?<name>.*?)', function(string $name) { return view('json', 'Hello ' . $name); }); Router::get('/users/(?<user_id>\d+)/tasks/(?<task_id>\d+)', function(int $user_id, int $task_id) { return view('json', [ 'user_id' => $user_id, 'task_id' => $task_id, ]); });
响应
响应是通过 EasyAPI\Response 类实现的,所有添加到路由中的方法和匿名函数都必须添加此类的响应,否则会失败。为了方便工作,在辅助函数中有一个名为 view 的函数,它接受与 EasyAPI\Response 相同的参数并返回对象。
<?php Router::post('/users', function() { return view('json', 'User created succesfully', 201); });
O
<?php Router::post('/users', function() { return new EasyAPI\Response('json', 'User created succesfully', 201); });
EasyAPI\Response 可以接收的参数是我们要返回的数据类型,可以是 json、raw(用于自定义响应)和 html,第二个参数是用户的响应,可以是字符串,对于 json 格式,我们可以发送一个数组,它将被转换为 JSON。第三个参数是 HTTP 状态码,默认为 200,最后我们可以添加额外的头信息,格式为键值对。
<?php Router::get('/users', function() { $response = ' <?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>cosasdedevs.com</title><link>https://cosasdedevs.com/feed/</link><description>En cosasdedevs.com encontrarás tutoriales sobre Python, Django, PHP y Laravel</description><atom:link href="https://cosasdedevs.com/feed" rel="self"></atom:link><language>es</language><lastBuildDate>Sun, 17 Apr 2022 06:17:08 +0000</lastBuildDate><item><title>Guía para aprender a trabajar con APIs</title><link>https://cosasdedevs.com/posts/guia-aprende-trabajar-con-apis/</link><description>Con esta guía aprenderás todas las partes implicadas en el funcionamiento de una API, el protocolo HTTP y buenas prácticas para construir una API</description><guid>https://cosasdedevs.com/posts/guia-aprende-trabajar-con-apis/</guid></item></channel></rss> '; $headers = [ 'content-type' => 'application/xml' ]; return new EasyAPI\Response('raw', $response, 200, $headers); });
对于 json 格式,响应是预先格式化的。如果我们发送一个小于 400 的状态码,将显示以下响应
{ "status": "success", "data": "<la respuesta que enviemos>" }
如果状态码错误,则响应将是
{ "status": "error", "error": "<la respuesta que enviemos>" }
中间件
路由允许在执行我们的控制器之前添加中间件以进行额外验证。我们创建的每个中间件都必须扩展类 EasyAPI\Middleware,并接收和返回一个对象 EasyAPI\Request。我们可以使用此对象来保存从中间件获取的信息,然后将其发送到控制器。
以下是一个中间件示例,用于验证基于JWT的token认证,其中保存了用户ID。
<?php namespace App\V1\Middlewares; use EasyAPI\Middleware; use EasyAPI\Request; use Firebase\JWT\JWT; use Firebase\JWT\Key; use Firebase\JWT\ExpiredException; use Exception; use EasyAPI\Exceptions\HttpException; class BasicAuth extends Middleware { public function handle(Request $request): Request { if(empty($_SERVER['HTTP_AUTHORIZATION'])) { throw new HttpException('You must send Authorization header', 422); } $token = $_SERVER['HTTP_AUTHORIZATION']; try { $decoded = JWT::decode($token, new Key($_ENV['JWT_KEY'], 'HS256')); $request->setData('user_id', $decoded->data->id); return $request; } catch(ExpiredException $e) { throw new HttpException('Your token has expired, please login again', 401); } catch(Exception $e) { throw new HttpException('An error has ocurred, please, make again login, if persists, contact with admin'); } } }
之后,我们可以在控制器内访问存储在 Request 中的信息,通过接收类 App 在每次执行路由时负责发送的 Request 参数。
// Rutas, BasicAuth sería nuestro middleware el cual enviamos como tercer parámetro en la ruta. . . . Router::get('/v1/tasks/(?<id>\d+)', TaskController::class . '@show', BasicAuth::class); . . . // Controlador TaskController public function show(int $id, Request $request) { $user_id = $request->getData('user_id'); $ddbb = new DBTask(); $task = $ddbb->getTaskByUserId($id, $user_id); if(empty($task)) { throw new HttpException('Task not found', 404); } return view('json', $task); }
请注意,如果发送了路由参数,Request 总是最后一个参数。
Request
Request 类有两个方法,setData 用于以键值对格式保存信息,getData 用于根据键获取存储的信息。
<?php namespace EasyAPI; /** * Set data from middleware to later access it from a controller */ class Request { private $data = []; public function setData(string $key, $value) { $this->data[$key] = $value; } public function getData(string $key) { return $this->data[$key] ?? null; } }
HttpException
如果我们想抛出一个直接向用户发送消息的异常,我们可以使用 EAsyAPI\HttpException,它接收错误消息和HTTP状态码。EasyAPI 负责将其翻译并转换为用户格式为JSON的响应。
在之前使用的控制器中
public function show(int $id, Request $request) { $user_id = $request->getData('user_id'); $ddbb = new DBTask(); $task = $ddbb->getTaskByUserId($id, $user_id); if(empty($task)) { throw new HttpException('Task not found', 404); } return view('json', $task); }
如果我们找不到任务,则抛出带有消息和HTTP状态码404(未找到)的 HttpException,用户将收到此消息
{ "status": "error", "error": "Task not found" }
HTTP状态码:404