kilylabs/phprest

PHP Rest 框架。

0.2.5 2020-09-15 20:28 UTC

This package is auto-updated.

Last update: 2024-09-16 05:24:26 UTC


README

添加了对PHP 7.0的支持!

Phprest

Build Status Code Coverage Quality Score Software License

描述

类似REST的PHP微框架。

它基于Proton(与StackPhp兼容)微框架。

Phprest 只提供了构建您自己的架构和组装任何文件夹结构所需的最基本功能。它只是利用一些优秀的库在您的应用程序周围的一个薄层。

组件

技能

  • 依赖注入
  • 路由
  • 错误处理
  • 序列化
  • 反序列化
  • HATEOAS
  • API版本
  • 分页
  • 日志记录

目录

安装

通过composer安装。

{
    "require": {
        "phprest/phprest": "@stable"
    }
}

提示:您应该浏览phprest/phprest页面,选择要使用的稳定版本,避免使用@stable元约束。

用法

服务

有几个服务可以帮助您解决一些常见问题

这些都是独立的仓库。

设置

<?php
require __DIR__ . '/../vendor/autoload.php';

use Phprest\Config;
use Phprest\Response;
use Phprest\Application;
use Symfony\Component\HttpFoundation\Request;

# vendor name, current API version, debug
$config = new Config('vendor.name', '0.1', true);

$app = new Application($config);

$app->get('/{version:\d\.\d}/', function (Request $request) {
    return new Response\Ok('Hello World!');
});

$app->run();

配置

您应该检查Config类。

日志记录

<?php
use Phprest\Service\Logger\Config as LoggerConfig;
use Phprest\Service\Logger\Service as LoggerService;
use Monolog\Handler\StreamHandler;

$config = new Config('vendor.name', '0.1');

$loggerHandlers[] = new StreamHandler('path_to_the_log_file', \Monolog\Logger::DEBUG);

$config->setLoggerConfig(new LoggerConfig('phprest', $loggerHandlers));
$config->setLoggerService(new LoggerService());

与Stack一起使用

您可以通过registerMiddleware函数注册中间件。

$app->registerMiddleware('Jsor\Stack\JWT', [
    [
        'firewall' => [
	    ['path' => '/',         'anonymous' => false],
	    ['path' => '/tokens',   'anonymous' => true]
	],
	'key_provider' => function() {
	    return 'secret-key';
	},
	'realm' => 'The Glowing Territories'
    ]
]);

API版本

Phprest默认支持API版本。这意味着ApiVersion Middleware会操作传入的请求。版本(基于当前的Accept头)被添加到路径中。

这意味着什么?

* 它不是重定向或转发方法,它只是通过中间件进行内部应用程序路由。

API 版本只能是以下范围之一

  • 0 - 9
  • 0.0 - 9.9
  • 如果Accept头不可解析

  • 那么Phprest将抛出不可接受异常

  • 如果您进行反序列化且Content-Type头不可解析

  • 那么Phprest将抛出不支持的媒体类型异常

路由

有关更多信息,请访问League/Route

简单路由

<?php
$app->get('/{version:\d\.\d}/hello', function (Request $request, $version) {
	# You can leave the $request and the $version variable
    return new Response\Ok('Hello World!');
});
  • ApiVersion Middleware每次都会操作内部路由,因此您必须注意路由的第一部分作为版本号。
  • 此路由在所有API版本中可用(请参阅\d\.\d正则表达式)
  • 您还可以设置固定的API版本号,例如 '/3.6/hello'

带有参数的路由

<?php
$app->get('/2.4/hello/{name:word}', function (Request $request, $name) {
    return new Response\Ok('Hello ' . $name);
});
  • 此路由仅在API版本2.4中可用

通过控制器进行路由

<?php
# index.php

# calls index method on HomeController class
$app->get('/{version:\d\.\d}/', '\Foo\Bar\HomeController::index');
<?php namespace Foo\Bar;
# HomeController.php

use Symfony\Component\HttpFoundation\Request;
use Phprest\Response;

class HomeController
{
    public function index(Request $request, $version)
    {
        return new Response\Ok('Hello World!');
    }
}

通过服务控制器进行路由

<?php
$app['HomeController'] = function () {
    return new \Foo\Bar\HomeController();
};

$app->get('/{version:\d\.\d}/', 'HomeController::index');

使用注解进行路由

您必须注册您的控制器

<?php

$app->registerController('\Foo\Bar\Controller\Home');
<?php namespace Foo\Bar\Controller;
# Home.php

use Phprest\Util\Controller;
use Symfony\Component\HttpFoundation\Request;
use Phprest\Response;
use Phprest\Annotation as Phprest;

class Home extends Controller
{
    /**
     * @Phprest\Route(method="GET", path="/foobars/{id}", since=1.2, until=2.8)
     */
    public function get(Request $request, $version, $id)
    {
        return new Response\Ok('Hello World!');
    }
}
  • since 标签是可选的
  • until 标签是可选的

控制器

要创建Phprest控制器,只需从 \Phprest\Util\Controller 扩展您的类。

<?php namespace App\Module\Controller;

class Index extends \Phprest\Util\Controller
{
   public function index(Request $request)
   {
      # ...
   }
}

序列化、反序列化、Hateoas

  • Phprest会根据Accept头自动序列化*您的响应。
  • Phprest可以根据Content-Type头反序列化您的内容。

除外*

  • 如果您的响应不是Response实例(例如,它是一个简单的字符串)
  • 如果您的响应为空

序列化示例

让我们看看一个Temperature实体

您不必使用注解!您可以使用配置文件!浏览 Jms\Serializer Willdurand\Hateoas

<?php namespace Foo\Entity;

use JMS\Serializer\Annotation as Serializer;
use Hateoas\Configuration\Annotation as Hateoas;

/**
 * @Serializer\XmlRoot("result")
 *
 * @Hateoas\Relation(
 *      "self",
 *      href = @Hateoas\Route("/temperatures", parameters = {"id" = "expr(object.id)"}, absolute = false)
 * )
 */
class Temperature
{
    /**
     * @var integer
     * @Serializer\Type("integer")
     */
    public $id;

    /**
     * @var integer
     * @Serializer\Type("integer")
     */
    public $value;

    /**
     * @var \DateTime
     * @Serializer\Type("DateTime")
     * @Serializer\Since("2")
     * @Serializer\Exclude
     */
    public $created;

    /**
     * @param integer $id
     * @param integer $value
     * @param \DateTime $created
     */
    public function __construct($id = null, $value = null, \DateTime $created = null)
    {
        $this->id = $id;
        $this->value = $value;
        $this->created = $created;
    }
}

路由器

<?php
$app->post('/{version:\d\.\d}/temperatures', function () use ($app, $version) {
    $temperature = new \Foo\Entity\Temperature(1, 32, new \DateTime());
    
    return new Response\Created('/temperatures/1', $temperature);
});

JSON响应(Accept: application/vnd.vendor+json; version=1)

{
    "id": 1,
    "value": 32,
    "_links": {
        "self": {
            "href": "\/temperatures\/1"
        }
    }
}

XML响应(Accept: application/vnd.vendor+xml; version=1)

<result>
  <id>1</id>
  <value>32</value>
  <link rel="self" href="/temperatures/1"/>
</result>

属性将默认从驼峰式转换为下划线名称,例如 camelCase -> camel_case。如果您想使用自定义序列化名称,必须在您的属性上使用 @SerializedName 选项。

反序列化示例

您必须在控制器中使用 HATEOAS Util 特性来进行反序列化。

# ...
use JMS\Serializer\Exception\RuntimeException;
# ...
    public function post(Request $request)
    {
        try {
            /** @var \Foo\Entity\Temperature $temperature */
            $temperature = $this->deserialize('\Foo\Entity\Temperature', $request);
        } catch (RuntimeException $e) {
            throw new Exception\UnprocessableEntity(0, [new Service\Validator\Entity\Error('', $e->getMessage())]);
        }
    }
# ...

分页

<?php
# ...
use Hateoas\Representation\PaginatedRepresentation;
use Hateoas\Representation\CollectionRepresentation;
# ...
$paginatedCollection = new PaginatedRepresentation(
    new CollectionRepresentation([$user1, $user2, ...]),
    '/users', # route
    [],       # route parameters, should be $request->query->all()
    1,        # page, should be (int)$request->query->get('page')
    10,       # limit, should be (int)$request->query->get('limit')
    5,        # total pages
    'page',   # page route parameter name, optional, defaults to 'page'
    'limit',  # limit route parameter name, optional, defaults to 'limit'
    true,     # absolute URIs
    47        # total number of rows
);
# ...
return new Response\Ok($paginatedCollection);

有关更多信息,请访问 HATEOAS 文档

响应

默认情况下,您可以使用几个响应,其中一个响应是Ok响应。

1xx, 2xx, 3xx 状态码

这些都是简单的Response对象。

示例

<?php
# ...
$app->get('/', function (Request $request) {
    return new Response\Ok('Hello World!');
});
# ...

类型

4xx, 5xx 状态码

这些都是异常。

示例

<?php
# ...
$app->get('/', function (Request $request) {
    # ...
    
    throw new \Phprest\Exception\BadRequest();
    
    # ...
});
# ...

类型

依赖注入容器

请参阅 Proton的文档,并请访问 League/Container 获取更多信息。

命令行界面

如果您想在安装composer后使用辅助脚本,可以使用(vendor/bin/phprest)。

您必须为脚本提供(启动的)应用程序实例。您有两种选择

  • 将应用程序实例放入特定文件: app/app.php
  • 您必须在适当的文件中返回启动的应用程序实例
  • 将应用程序实例的路径放入 paths.php 文件中
  • 您必须从 paths.php 文件返回一个数组,其中包含 app 数组键下的应用程序文件路径

错误处理器

Phprest使用 League\BooBoo 处理错误。默认格式化器是 Json和Xml格式化器

单个异常

<?php
# ...
$app->get('/{version:\d\.\d}/', function (Request $request, $version) {
    throw new \Phprest\Exception\Exception('Code Red!', 9, 503);
});
# ...

响应进行了内容协商(xml/json),状态码是503。

{
    "code": 9,
    "message": "Code Red!",
    "details": []
}
<result>
    <code>9</code>
    <message>
        <![CDATA[Code Red!]]>
    </message>
</result>

身份验证

基本身份验证

您需要这个包

$app->registerMiddleware('Dflydev\Stack\BasicAuthentication', [
    [
        'firewall' => [
	    ['path' => '/', 'anonymous' => false],
            ['path' => '/temperatures', 'method' => 'GET', 'anonymous' => true]
	],
	'authenticator' => function ($username, $password) {
            if ('admin' === $username && 'admin' === $password) {
                # Basic YWRtaW46YWRtaW4=
                return 'success';
            }
        },
	'realm' => 'The Glowing Territories'
    ]
]);

JWT身份验证

您需要这个包

$app->registerMiddleware('Jsor\Stack\JWT', [
    [
        'firewall' => [
	    ['path' => '/',         'anonymous' => false],
	    ['path' => '/tokens',   'anonymous' => true]
	],
	'key_provider' => function() {
	    return 'secret-key';
	},
	'realm' => 'The Glowing Territories'
    ]
]);

API测试

有几个很好的工具可以用来测试您的API。

  • PostmanNewman
  • 提示:在Postman中创建集合,然后在Newman中运行这些操作
  • Frisby
  • Frisby是一个基于node.js和Jasmine构建的REST API测试框架,它使测试API端点变得简单、快速且有趣。
  • Runscope
  • 用于API监控和测试

API文档

只是一些建议

  • API Blueprint
    • API Blueprint是一种面向文档的API描述语言。它对纯Markdown做了一些语义假设。
  • Swagger
    • 通过启用Swagger的API,您将获得交互式文档、客户端SDK生成和可发现性。