tomaj / nette-api
Nette api
Requires
- php: >= 7.1.0
- ext-curl: *
- ext-filter: *
- ext-json: *
- ext-session: *
- justinrainbow/json-schema: ^5.2
- league/fractal: ~0.17
- nette/application: ^3.0
- nette/http: ^3.0
- tomaj/nette-bootstrap-form: ^2.0
- tracy/tracy: ^2.6
Requires (Dev)
- latte/latte: ^2.4 | ^3.0
- nette/di: ^3.0
- phpunit/phpunit: >7.0 <10.0
- squizlabs/php_codesniffer: ^3.2
- symfony/yaml: ^4.4|^5.0|^6.0
- dev-master
- 4.0.x-dev
- 2.11.0
- 2.10.1
- 2.10.0
- 2.9.0
- 2.8.0
- 2.7.0
- 2.6.0
- 2.5.0
- 2.4.0
- 2.3.1
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.1
- 2.0.0
- 1.18.0
- 1.17.0
- 1.16.0
- 1.15.0
- 1.14.0
- 1.13.2
- 1.13.1
- 1.13.0
- 1.12.0
- 1.11.0
- 1.10.0
- 1.9.1
- 1.9.0
- 1.8.1
- 1.8.0
- 1.7.0
- 1.6.2
- 1.6.1
- 1.6.0
- 1.5.0
- 1.4.0
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.1
- 1.0.0
- 0.1.0
- 0.0.1
- dev-dev-1.x
- dev-additional-data-for-api-handlers
- dev-additional-data-for-handlers
This package is auto-updated.
Last update: 2024-08-28 12:35:19 UTC
README
Nette 简单 API 库
为什么选择 Nette-Api
此库为 Nette 框架提供开箱即用的 API 解决方案。您可以注册 API 端点并将其连接到指定的处理程序。您只需实现您自己的业务逻辑。库为您提供了 API 的授权、验证、速率限制和格式化服务。
安装
此库需要 PHP 7.1 或更高版本。
推荐安装方法是使用 Composer
composer require tomaj/nette-api
库符合 PSR-1、PSR-2、PSR-3 和 PSR-4。
Nette-API 的工作原理
首先,您必须为路由注册库表示器。在 config.neon 中添加此行
application: mapping: Api: Tomaj\NetteApi\Presenters\*Presenter
然后添加路由到您的 RouterFactory
$router[] = new Route('/api/v<version>/<package>[/<apiAction>][/<params>]', 'Api:Api:default');
如果您想使用 RESTful URL,您还需要另一个路由
$router[] = new Route('api/v<version>/<package>/<id>', [ 'presenter' => 'Api:Api', 'action' => 'default', 'id' => [ Route::FILTER_IN => function ($id) { $_GET['id'] = $id; return $id; } ], ]);
之后,您只需将您的 API 处理程序注册到 apiDecider ApiDecider,注册 ApiLink 和 Tomaj\NetteApi\Misc\IpDetector。这也可以通过 config.neon 完成
services: - Tomaj\NetteApi\Link\ApiLink - Tomaj\NetteApi\Misc\IpDetector apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\NoAuthorization()) - addApi(\Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'users', 'send-email'), \App\MyApi\v1\Handlers\SendEmailHandler(), \Tomaj\NetteApi\Authorization\BearerTokenAuthorization())
如示例所示,您可以注册任意数量的端点,并具有不同的配置。Nette-Api 从一开始就支持 API 版本控制。此示例将准备以下 API 调用
http://yourapp/api/v1/users
- 可通过 GET 获取http://yourapp/api/v1/users/send-email
- 可通过 POST 获取
Nette-Api 的核心是处理程序。对于此示例,您需要实现两个类
- App\MyApi\v1\Handlers\UsersListingHandler
- App\MyApi\v1\Handlers\SendEmailHandler
这些处理程序实现接口 ApiHandlerInterface,但为了更方便的使用,您可以从 BaseHandler 扩展您的处理程序。当有人访问您的 API 时,这些处理程序将被触发,并将调用 handle() 方法。
namespace App\MyApi\v1\Handlers; use Tomaj\NetteApi\Handlers\BaseHandler; use Tomaj\NetteApi\Response\JsonApiResponse; use Tomaj\NetteApi\Response\ResponseInterface; class UsersListingHandler extends Basehandler { private $userRepository; public function __construct(UsersRepository $userRepository) { parent::__construct(); $this->userRepository = $userRepository; } public function handle(array $params): ResponseInterface { $users = []; foreach ($this->userRepository->all() as $user) { $users[] = $user->toArray(); } return new JsonApiResponse(200, ['status' => 'ok', 'users' => $users]); } }
此简单的处理程序使用了由 Nette 容器创建的 UsersRepository(因此您必须在 config.neon 中注册您的 App\MyApi\v1\Handlers\UsersListingHandler)。
高级用法(与 Fractal 结合使用)
Nette-Api 提供了与 Fractal 库的集成,用于格式化 API 响应。如果您想使用它,您必须从 BaseHandler 扩展您的处理程序,并且您的 Fractal 实例将可通过 $this->getFractal()
访问。
Fractal 的主要优势是将您的 API 的“视图”(例如将数据转换为 JSON 对象或 XML 或其他任何内容)分离出来。您还可以在其他转换中包含转换,以将其他对象包含到其他对象中。
使用 Fractal 的示例
- 您需要 Transformer
namespace App\MyApi\v1\Transformers; use League\Fractal\TransformerAbstract; class UserTransformer extends TransformerAbstract { public function transform($user) { return [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name, ]; } }
- 这将您的处理程序
namespace App\MyApi\v1\Handlers; use Tomaj\NetteApi\Handlers\BaseHandler; use Tomaj\NetteApi\Response\JsonApiResponse; use Tomaj\NetteApi\Response\ResponseInterface; class UsersListingHandler extends Basehandler { private $userTransformer; public function __construct(UserTransformer $userTransformer) { parent::__construct(); $this->userTransformer = $userTransformer; } public function handle(array $params): ResponseInterface { $users = $this->useRepository->all(); $resource = new Collection($users, $this->userTransformer); $result = $this->getFractal()->createData($resource)->toArray(); return new JsonApiResponse(200, $result); } }
我们建议您查看 Fractal 库。有关转换器、序列化器、分页等信息,库中有很多信息。这是一个非常好的库。
在 latte 中使用 ApiLink
首先,您必须在 config.neon 中注册过滤器
services: apiLink: Tomaj\NetteApi\Link\ApiLink() latte.latteFactory: setup: - addFilter(apiLink, [@apiLink, link])
注意:过滤器的名称必须是 apiLink
,因为它在宏/扩展中使用。
对于 latte < 3.0 注册 latte 宏
latte: macros: - Tomaj\NetteApi\Link\ApiLinkMacro
对于 latte >= 3.0 注册 latte 扩展
latte: extensions: - Tomaj\NetteApi\Link\ApiLinkExtension
在 latte 文件中的使用
{apiLink $method, $version, $package, $apiAction, ['title' => 'My title', 'data-foo' => 'bar']}
端点输入
每个处理器可以描述所需的输入。可以是 GET 或 POST 参数,也可以是 COOKIES、原始 POST、JSON 或文件上传。您必须实现方法 params()
,在该方法中,您必须返回包含参数的数组。这些参数在 API 控制台中用于生成表单。
用户详情示例
namespace App\MyApi\v1\Handlers; use Tomaj\NetteApi\Handlers\BaseHandler; use Tomaj\NetteApi\Params\GetInputParam; use Tomaj\NetteApi\Response\JsonApiResponse; use Tomaj\NetteApi\Response\ResponseInterface; class UsersDetailHandler extends Basehandler { private $userRepository; public function __construct(UsersRepository $userRepository) { parent::__construct(); $this->userRepository = $userRepository; } public function params(): array { return [ (new GetInputParam('id'))->setRequired(), ]; } public function handle(array $params): ResponseInterface { $user = $this->userRepository->find($params['id']); if (!$user) { return new JsonApiResponse(404, ['status' => 'error', 'message' => 'User not found']); } return new JsonApiResponse(200, ['status' => 'ok', 'user' => [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name, ]); } }
输入类型
Nette-Api 提供了各种 InputParam 类型。您可以通过 GET、POST、COOKIES、FILES、RAW POST 数据或 JSON 发送参数。所有输入类型都可通过测试控制台访问。
这是支持输入类型的表格
输出
通过为您的处理器实现输出方法,您可以指定可能的输出列表(例如输出架构),这些将在响应发送给用户之前进行验证。如果没有设置输出,则不进行验证即发送响应给用户。
使用示例
public function outputs(): array { $schema = [ 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string', ], 'surname' => [ 'type' => 'string', ], 'sex' => [ 'type' => 'string', 'enum' => ['M', 'F'], ], ], 'required' => ['name', 'surname'], 'additionalProperties' => false, ]; return [ new JsonOutput(200, json_encode($schema)), ]; }
有关更多示例,请参阅 JSON schema 网页。请注意,Nette API 使用 justinrainbow/json-schema 来验证架构,并且此包仅支持 json schema draft 03 和 04。
安全性
使用 Nette-Api 保护您的 API 很容易。您必须实现自己的 授权(Tomaj\NetteApi\Authorization\ApiAuthorizationInterface)或使用预定义的实现,并将其作为第三个参数添加到 config.neon 中的 addApi() 方法中。
基本身份验证
基本身份验证是内置在 HTTP 协议中的简单身份验证方案。它包含用户名和密码。您可以定义任意多的用户名和密码对。但每个用户名只有一个密码。
services: apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\BasicBasicAuthentication(['first-user': 'first-password', 'second-user': 'second-password']))
Bearer 令牌身份验证
对于使用少量令牌的简单 Bearer 令牌授权,您可以使用 StaticTokenRepository(Tomaj\NetteApi\Misc\StaticTokenRepository)。
services: staticTokenRepository: Tomaj\NetteApi\Misc\StaticTokenRepository(['dasfoihwet90hidsg': '*', 'asfoihweiohgwegi': '127.0.0.1']) apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\BearerTokenAuthorization(@staticTokenRepository))
通过此注册,您将拥有可从任何地方通过带有 Authorisation HTTP 标头 Bearer dasfoihwet90hidsg
访问的 api /api/v1/users
,或从 127.0.0.1 通过 Bearer asfoihweiohgwegi
访问。在 Nette-Api 中,如果您想为令牌指定 IP 限制,可以使用此模式
但实现您自己的 API 授权非常容易。
API 密钥
您还可以使用 API 密钥进行授权。API 密钥是在客户端进行 API 调用时提供的令牌。密钥可以发送在查询字符串、标头或 cookie 中。请参见以下示例
services: staticTokenRepository: Tomaj\NetteApi\Misc\StaticTokenRepository(['dasfoihwet90hidsg': '*', 'asfoihweiohgwegi': '127.0.0.1']) apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users', 'query'), Tomaj\NetteApi\Authorization\QueryApiKeyAuthentication('api_key', @staticTokenRepository)) - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users', 'header'), Tomaj\NetteApi\Authorization\HeaderApiKeyAuthentication('X-API-KEY', @staticTokenRepository)) - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users', 'cookie'), Tomaj\NetteApi\Authorization\CookieApiKeyAuthentication('api_key', @staticTokenRepository))
速率限制
此库提供简单的 API 速率限制接口。您需要做的就是像以下示例中那样实现此接口
use Nette\Application\Responses\TextResponse; use Tomaj\NetteApi\RateLimit\RateLimitInterface; use Tomaj\NetteApi\RateLimit\RateLimitResponse; class MyRateLimit implements RateLimitInterface { public function check(): ?RateLimitResponse { // do some logic here // example outputs: return null; // no rate limit return new RateLimitResponse(60, 50); // remains 50 of 60 hits return new RateLimitResponse(60, 0, 120); // remains 0 of 60 hits, retry after 120 seconds return new RateLimitResponse(60, 0, 120, new TextResponse('My custom error message')); // remains 0 of 60 hits, retry after 120 seconds, with custom TextResponse (default is Json response, see ApiPresenter::checkRateLimit()) } }
然后您必须将 API 注册到带有速率限制的 ApiDecider 中
services: apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), \Tomaj\NetteApi\Authorization\BearerTokenAuthorization(@staticTokenRepository), MyRateLimit())
JavaScript ajax 调用(CORS - 预检 OPTIONS 调用)
如果您需要从其他域通过 JavaScript ajax 调用 API,您需要为 预检调用中的 OPTIONS 方法 准备 API。Nette-api 已经为此情况做好了准备,并且您可以选择是否全局启用预检调用,或者您可以注册预定义的预检处理器。
全局启用 - 每个 API 端点都将可用于预检 OPTIONS 调用
services: apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - enableGlobalPreflight() - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), Tomaj\NetteApi\Authorization\NoAuthorization())
或者您可以注册自定义的 OPTIONS 端点
services: apiDecider: factory: Tomaj\NetteApi\ApiDecider setup: - addApi(\Tomaj\NetteApi\EndpointIdentifier('OPTIONS', 1, 'users'), \Tomaj\NetteApi\Handlers\CorsPreflightHandler(), Tomaj\NetteApi\Authorization\NoAuthorization()) - addApi(\Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'users'), \App\MyApi\v1\Handlers\UsersListingHandler(), Tomaj\NetteApi\Authorization\NoAuthorization())
日志记录
如果你的API提供了有价值的信息,记录API访问是一种良好的实践。要启用记录,您需要实现具有接口的类 ApiLoggerInterface(Tomaj\NetteApi\Logger\ApiLoggerInterface),并在 config.neon 中将其注册为服务。它将在所有API请求执行后自动绑定并调用。
CORS安全
如果您需要使用JavaScript与API交互,则需要发送正确的CORS头。ApiPresenter 有属性可以设置这些头。默认情况下,API会发送带有值 ''* 的头 'Access-Control-Allow-Origin'。如果您需要更改它,可以将属性 $corsHeader 设置为以下值:
- 'auto' - 返回带有请求域的 'Access-Control-Allow-Origin' 头。这不够安全,但您可以通过AJAX从其他域名访问此API
- ''* - 发送带有 '*' 的头 - 如果您不使用具有 xhrFields: { withCredentials: true } 设置的 $.ajax 发送cookie到API,这将正常工作
- 'off' - 不会发送任何CORS头
- 其他 - 其他任何值都将发送到 'Access-Control-Allow-Origin' 头中
您可以在注册 ApiPresenter 时在 config.neon 中设置此属性
services: - factory: Tomaj\NetteApi\Presenters\ApiPresenter setup: - setCorsHeader('auto')
或者如果您扩展了 ApiPresenter,那么您可以在自己的展示器中设置它。
WEB控制台 - API测试器
Nette-Api 包含2个UI控件,可用于验证API。它将生成所有API调用的列表,并自动生成带有所有API参数的表单。
所有组件都生成bootstrap html,并且可以使用bootstrap css进行样式化
您必须在控制器中创建组件
use Nette\Application\UI\Presenter; use Tomaj\NetteApi\ApiDecider; use Tomaj\NetteApi\Component\ApiConsoleControl; use Tomaj\NetteApi\Component\ApiListingControl; use Tomaj\NetteApi\Link\ApiLink; class MyPresenter extends Presenter { private $apiDecider; private $apiLink; private $method; private $version; private $package; private $apiAction; public function __construct(ApiDecider $apiDecider, ApiLink $apiLink = null) { parent::__construct(); $this->apiDecider = $apiDecider; $this->apiLink = $apiLink; } public function renderShow(string $method, int $version, string $package, ?string $apiAction = null): void { $this->method = $method; $this->version = $version; $this->package = $package; $this->apiAction = $apiAction; } protected function createComponentApiListing(): ApiListingControl { $apiListing = new ApiListingControl($this->apiDecider); $apiListing->onClick[] = function ($method, $version, $package, $apiAction) { $this->redirect('show', $method, $version, $package, $apiAction); }; return $apiListing; } protected function createComponentApiConsole() { $api = $this->apiDecider->getApi($this->method, $this->version, $this->package, $this->apiAction); $apiConsole = new ApiConsoleControl($this->getHttpRequest(), $api->getEndpoint(), $api->getHandler(), $api->getAuthorization(), $this->apiLink); return $apiConsole; } }
故障排除
如果您的Apache服务器在CGI或fastCGI脚本上运行,$_SERVER['HTTP_AUTHORIZATION'] 为空。您需要执行一些 mod_rewrite 巧技以将头通过CGI障碍传递,如下所示:
RewriteEngine on
RewriteRule .? - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]`
变更日志
有关最近更改的详细信息,请参阅 CHANGELOG。
测试
$ composer test
贡献
有关详细信息,请参阅 CONTRIBUTING 和 CONDUCT。
安全性
如果您发现任何安全相关的问题,请通过电子邮件 tomasmajer@gmail.com 而不是使用问题跟踪器。
许可证
MIT许可证(MIT)。有关更多信息,请参阅 许可证文件。