jobbely/phprest

PHP Rest 框架。

0.2.0 2016-10-27 18:43 UTC

This package is not auto-updated.

Last update: 2024-10-02 20:05:59 UTC


README

Author Build Status Coverage Status Quality Score Software License Packagist Version Total Downloads

游戏结束

此项目不再维护!

描述

类似 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 中间件 会处理传入的请求。版本(基于当前 Accept 头)会被添加到路径中。

这意味着什么?

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

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

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

  • 那么 Phprest 会抛出 Not Acceptable 异常

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

  • 那么 Phprest 会抛出 Unsupported Media Type 异常

路由

有关更多信息,请访问 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 中间件 会每次处理内部路由,因此您必须关注路由的第一部分作为版本号。
  • 此路由在所有 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实例(例如,它是一个简单的字符串)
  • 如果您的响应为空

序列化示例

让我们看看一个温度实体

您不必使用注解!您可以使用配置文件!在 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生成和可发现性。