johnvandeweghe/lunixrest

此软件包的最新版本(v3.2.0)没有提供许可证信息。

轻量级REST服务器

v3.2.0 2017-06-04 06:37 UTC

README

Build Status Code Coverage Scrutinizer Code Quality

概述

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"
        ]);
    }
}