albertorc87/easyapi

EasyAPI 是一个针对开发 REST 架构 API 的微型框架,不依赖任何库。

安装: 54

依赖者: 0

建议者: 0

安全性: 0

星标: 4

关注者: 2

分支: 2

开放问题: 0

类型:package

v0.1.5 2022-05-16 16:42 UTC

This package is auto-updated.

Last update: 2024-09-16 21:40:16 UTC


README

EasyAPI 是一个针对开发 REST 架构 API 的微型框架,不依赖任何库。

学习如何使用 APIs 的指南

如果文档中讨论的许多内容您还不清楚,我可以提供这个指南,其中解释了如何使用 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_MODEROOT_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 可以接收的参数是我们要返回的数据类型,可以是 jsonraw(用于自定义响应)和 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

关于此框架的更多信息即将到来