johnvandeweghe / lunixrest
轻量级REST服务器
Requires
- php: ^7.1
- ext-mbstring: *
- psr/cache: ^1.0
- psr/http-message: ^1.0
- psr/log: ^1.0
Requires (Dev)
- phpunit/phpunit: ^6.0
Suggests
- johnvandeweghe/lunixrest-basics: Everything you need to create a basic API
- monolog/monolog: A PSR-3 implementation that supports logging to nearly everything
This package is not auto-updated.
Last update: 2024-09-27 22:21:39 UTC
README
概述
LunixREST是一个高度可扩展、轻量级的PHP(7.1+)编写REST API的库。
其主要目标是允许创建一个REST API,其中每个部分的行为都可以被重写,而无需任何代码黑客技术。因此,它通常与现有的代码库非常兼容。
特性
- API密钥访问控制
- 端点路由
- API版本控制
- API访问节流
- PSR-2风格
- PSR-3(日志)兼容
- PSR-4(自动加载)兼容
- PSR-6(缓存)兼容
- PSR-7(http)兼容
- 灵活的输出格式化
有关一些基本实现和示例,请参阅https://github.com/johnvandeweghe/LunixREST-Basics。
项目标准
单元测试覆盖率
该项目力求始终尽可能接近100%的代码覆盖率。这在数字上,以及在实际的代码路径覆盖上。如果情况不是这样,请留下一个问题,它将尽快得到解决。
贡献
对项目的贡献非常受欢迎。只需提交一个包含所需更改/添加的拉取请求。最好通过留下一个问题来让我知道你在做什么。所有不符合此处概述标准的PR将不会合并,直到所有差异得到解决。
代码风格
如上所述的特性列表中所述,此项目尽可能遵守PSR-2。不使用自动格式化工具,因此可能会有违反此规定的代码部分。请随意提交一个问题票据,我会处理它们。
命名空间
命名空间遵循PSR-4。此外,命名空间应按依赖关系嵌套。这意味着,如果Server是唯一使用Widget的类,那么Widget应位于Server命名空间内。交叉依赖应导致依赖的类不比根更近。
安装
要求
所有依赖关系都在composer.json中指定,因此只要使用Composer与此库一起使用,所有依赖关系都应该得到解决。
话虽如此,以下是一些依赖关系
- PHP 7.1+
- php-mbstring
- PSR/Cache
- PSR/HTTP-Message
- PSR/Log
版本说明
该项目定期更新master。master中的更改如果不发布,则不保证稳定,应如此处理。对于生产项目,请使用发布标签。
所有次要版本号更新都将保证向后兼容性。主要版本更改不会遵守该标准,但通常目标是最大限度地减少接口更改。
安装
该项目在Packigist中列出,是Composer的默认存储库。因此安装非常简单
composer require johnvandeweghe/lunixrest
使用
HTTPServer
LunixREST的实现基础是HTTPServer类。此类接受PSR-7 ServerRequestInterface并返回一个可输出到SAPI的ResponseInterface。
以下是一个使用Guzzle的PSR-7实现(如果您使用LunixREST-Basics,它已经包含在内)的示例。
$httpServer = new \LunixREST\HTTPServer($server, $requestFactory, $logger); $serverRequest = \GuzzleHttp\Psr7\ServerRequest::fromGlobals(); \LunixREST\HTTPServer::dumpResponse($httpServer->handleRequest($serverRequest, new GuzzleHttp\Psr7\Response()));
看起来很简单,除了显然缺失的变量定义,如 $server、$requestFactory 和 $logger。HTTPServer 需要这些变量来构建,所以让我们一步一步地构建它们作为示例。
服务器/GenericServer
服务器的工作是接收由 HTTPServer 生成的 APIRequest,并返回一个 APIResponse(如果适用,也可以抛出异常)
但服务器是一个接口,因此我们需要一个具体的实现来使用。在这个例子中,我们将使用 GenericServer。GenericServer 是一个旨在尽可能从其他类中派生行为的实现。
$server = new GenericServer($accessControl, $throttle, $responseFactory, $router);
同样,很简单,但我们缺少一些定义,所以让我们逐一分解。
访问控制/PublicAccessControl
GenericServer 需要一个 AccessControl 实例来处理访问控制。
例如,PublicAccessControl 接收一个请求并声明它是允许的,而不对其进行任何检查。正如其名称所暗示的,它是用于公共 API 的,并且完全忽略密钥。
$accessControl = new \LunixREST\Server\AccessControl\PublicAccessControl();
节流/NoThrottle
GenericServer 还需要一个 Throttle 实例,如果需要的话,来处理节流请求。
例如,NoThrottle 只返回一个给定的请求永远不需要被节流。对于较小的 API 实现,这种适用性较低。实际的 Throttle 实现将可以在 LunixREST-Basics 中找到。
$throttle = new \LunixREST\Server\Throttle\NoThrottle();
响应工厂/RegisteredResponseFactory
GenericServer 还需要 ResponseFactory 的一个实例,它使用它将 APIResponseData 对象转换为 APIResponse。这个功能的关键是将 APIResponseData 对象中的数据转换,并将其转换为 PSR-7 StreamInterface。
例如,RegisteredResponseFactory 接收一个 APIResponseDataSerializers 的列表,并将它们与特定的 MIME 类型关联起来。
$responseFactory = new \LunixREST\Server\ResponseFactory\RegisteredResponseFactory([ 'application/json' => new \LunixRESTBasics\APIResponse\JSONResponseDataSerializer() ]);
你会注意到 JSONResponseDataSerializer 在 LunixREST-Basics 项目中。这是因为它需要一个特定的 PSR-7 实现(它使用 Guzzle 的)。由于这个原因,Core 中不包括实际的 APIResponseDataSerializer 实现。
路由器/GenericRouter
GenericServer 需要的最后一件事是路由器。路由器接收一个请求,并决定调用哪个端点和该端点上的哪个方法。然后它继续调用端点并返回结果。
在这个例子中,我们将使用 GenericRouter,它定义了一些基本的行为,但将大多数细节传递给 EndpointFactory,后者用于实际查找端点。
$router = new \LunixREST\Server\Router\GenericRouter($endpointFactory);
端点工厂/SingleEndpointFactory
EndpointFactory 从请求的端点名称和 APIRequest 的解析版本构建端点。
在这个例子中,我们将使用来自 Basics 仓库的 SingleEndpointFactory 实现。我们还在使用一个通用的 HelloWorld 端点,其代码将在示例之后提供。
$endpointFactory = new \LunixRESTBasics\Endpoint\SingleEndpointFactory(new HelloWorld());
use LunixREST\Server\APIResponse\APIResponseData; use LunixREST\Server\Router\Endpoint\DefaultEndpoint; use LunixREST\Server\Router\Endpoint\Exceptions\UnsupportedMethodException; use LunixREST\Server\APIRequest\APIRequest; class HelloWorld extends DefaultEndpoint { /** * @param APIRequest $request * @return APIResponseData * @throws UnsupportedMethodException */ public function getAll(APIRequest $request): APIResponseData { return new APIResponseData([ "helloworld" => "HelloWorld" ]); } }
这也是一个 API 可以利用扩展了包含的抽象类 LoggingEndpointFactory 和 CachingEndpointFactory 的 EndpointFactory 实现的时候。这些实现被编写为允许使用日志和缓存 PSR。
请求工厂
现在我们已经处理了服务器,HTTPServer 需要的下一件事是 RequestFactory 的一个实例。
RequestFactory 的任务是接收 PSR-7 ServerRequestInterface,并将其解析为 APIRequest。
通常,你将想要使用 GenericRequestFactory 并定义自己的 URLParser 和 HeaderParser。如果你只想使用预写的头解析器,你可以使用只需要 URLParser 并使用内置的 DefaultHeaderParser 的 DefaultRequestFactory。
然而,在这个例子中,我们将让它更简单一些,并使用 LunixREST-Basics 中的另一个类:BasicRequestFactory,它扩展了 DefaultRequestFactory 以使用 BasicURLParser。
BasicURLParser 期望请求看起来像这样
/VERSION/API_KEY/ENDPOINT[/OPTIONAL_ELEMENT].RESPONSE_TYPE_EXTENSION
或者为了测试我们的当前示例
/1.0/public/helloworld.json
最后,我们的示例代码
$requestFactory = new \LunixRESTBasics\APIRequest\RequestFactory\BasicRequestFactory();
LoggerInterface
HTTP服务器最后需要的是PSR LoggerInterface的一个实例。在这里这是必需的,因为PSR为那些不想记录日志的人提供了一个简单的解决方案:NullLogger。对于实际的日志记录,我们推荐使用Monolog。
$logger = new \Psr\Log\NullLogger();
完整示例
现在我们有了使用LunixREST定义基本API所需的一切。我们的API具有以下特性定义
- 公共
- 明确避免限流
- 可以写入JSON响应
- 通过单个端点(HelloWorld)处理所有请求
- 避免任何PSR-7请求体中间件,因此它只能处理HTTP urlencoded/form-data请求
该代码如下所示
$accessControl = new \LunixREST\Server\AccessControl\PublicAccessControl(); $throttle = new \LunixREST\Server\Throttle\NoThrottle(); $responseFactory = new \LunixREST\Server\ResponseFactory\RegisteredResponseFactory([ 'application/json' => new \LunixRESTBasics\APIResponse\JSONResponseDataSerializer() ]); $endpointFactory = new \LunixRESTBasics\Endpoint\SingleEndpointFactory(new \HelloWorld()); $router = new \LunixREST\Server\Router\GenericRouter($endpointFactory); $server = new \LunixREST\Server\GenericServer($accessControl, $throttle, $responseFactory, $router); $requestFactory = new \LunixRESTBasics\APIRequest\RequestFactory\BasicRequestFactory\BasicRequestFactory(); $logger = new \Psr\Log\NullLogger(); $httpServer = new \LunixREST\HTTPServer($server, $requestFactory, $logger); $serverRequest = \GuzzleHttp\Psr7\ServerRequest::fromGlobals(); \LunixREST\HTTPServer::dumpResponse($httpServer->handleRequest($serverRequest, new \GuzzleHttp\Psr7\Response()));
以及我们单个端点的代码
use LunixREST\Server\APIResponse\APIResponseData; use LunixREST\Server\Router\Endpoint\DefaultEndpoint; use LunixREST\Server\Router\Endpoint\Exceptions\UnsupportedMethodException; use LunixREST\Server\APIRequest\APIRequest; class HelloWorld extends DefaultEndpoint { /** * @param APIRequest $request * @return APIResponseData * @throws UnsupportedMethodException */ public function getAll(APIRequest $request): APIResponseData { return new APIResponseData([ "helloworld" => "HelloWorld" ]); } }