mediagone / symfony-easy-api
使用普通的 Symfony 控制器快速构建 JSON API。
1.0.0
2024-09-25 13:20 UTC
Requires
- php: ^7.4|^8.0
- ext-json: *
- symfony/http-foundation: ^4.0|^5.0|^6.0|^7.0
Requires (Dev)
- phpunit/phpunit: ^9.0
README
此包提供了辅助类,可以轻松地从普通的 Symfony 控制器构建 Json API。
支持的功能
- 单条和集合结果
- 集合结果的分页
- 开箱即用支持大多数有用的状态码(200、201、202、204、400、401、403、404、405、409、410、415、422、429、500 和 501)。
安装
此包需要 PHP 7.4+。
将其作为 Composer 依赖项添加
$ composer require mediagone/symfony-easy-api
简介
此包提供了一些类来处理 API 请求并返回结构化的 JSON 响应
ApiPayload200Success
ApiPayload201Created
ApiPayload202Accepted
ApiPayload204NoContent
ApiPayloadError400BadRequest
ApiPayloadError401Unauthorized
ApiPayloadError403Forbidden
ApiPayloadError404NotFound
ApiPayloadError405MethodNotAllowed
ApiPayloadError409Conflict
ApiPayloadError410Gone
ApiPayloadError415UnsupportedMediaType
ApiPayloadError422UnprocessableEntity
ApiPayloadError429TooManyRequests
ApiPayloadError500ServerError
ApiPayloadError501NotImplemented
示例
构建 API 控制器的最简单方法是使用 EasyApi
类,并将控制器代码包裹在匿名函数中,以利用自动错误处理。
1. 实例 API 端点
EasyApi->response
方法接受任何可调用的参数,该参数返回一个 ApiPayload
实例。
use App\Thing\ThingRepository; use Mediagone\Symfony\EasyApi\EasyApi; use Mediagone\Symfony\EasyApi\Payloads\ApiPayload200Success; use Mediagone\Symfony\EasyApi\Payloads\ApiPayloadError404NotFound; use Symfony\Component\HttpFoundation\Response; /** * @Route("/api/things/{thingId}", name="api_things", methods={"GET"}) */ final class ApiEndpointController { public function __invoke(int $thingId, EasyApi $easyApi, ThingRepository $thingRepository) : Response { return $easyApi->response( function() use ($thingId, $thingRepository) { // Any uncatched exception would be automatically converted into an ApiPayloadError500ServerError response's payload. if ($thingId < 1) { throw new LogicException("Invalid `\$thingId` value ($thingId)"); } $thing = $thingRepository->find($thingId); if ($thing === null) { // Explicit "not found" error response's payload return ApiPayloadError404NotFound::create('Thing not found (id: '.$thingId.')'); } return ApiPayload200Success::createWithSingleResult($thing); } ); } }
在成功的情况下,前面的控制器将返回以下 JSON 对象
{ "success": true, "status": "ok", "statusCode": 200, "payload": { "result": { "id": 1, "name": "First thing" } } }
或一个 "未找到" 的响应
{ "success": false, "status": "not_found", "statusCode": 404, "error": "not_found", "errorDescription": "Thing not found (id: 1)", "errorCode": 0 }
或一个 "服务器错误" 的响应
{ "success": false, "status": "server_error", "statusCode": 500, "error": "server_error", "errorDescription": "Unexpected server error: Invalid `$thingId` value (-1)", "errorCode": 0 }
注意:errorCode
是 PHP 异常的内部错误代码(默认为 0)。您通常可以通过向构造函数传递一个额外的整数参数来定义它,例如 throw new LogicException("Invalid
$thingId value ($thingId)", 1234);
。
2. 实例集合 API 端点
您还可以通过使用 ApiPayload200Success::createWithArrayResult
工厂方法返回多个结果
/** * @Route("/api/things", name="api_things", methods={"GET"}) */ final class ApiEndpointController { public function __invoke(EasyApi $easyApi, ThingRepository $thingRepository) : Response { return $easyApi->response( function() use ($thingRepository) { $things = $thingRepository->findAll(); return ApiPayload200Success::createWithArrayResult($things); } ); } }
这将导致一个略有不同的 JSON 对象
{ "success": true, "status": "ok", "statusCode": 200, "payload": { "results": [ { "id": 1, "name": "First thing" }, { "id": 2, "name": "Second thing" }, { "id": 3, "name": "Third thing" } ], "resultsCount": 3, "resultsCountTotal": 3, "page": 1, "pageCount": 1 } }
3. 集合分页
当处理大量的数据库条目时,您可能希望分页结果以分块检索它们。
该包提供了 ApiPagination
类来帮助实现此功能。
它需要两个数据库查询:一个用于计算结果总数,另一个用于获取请求的结果
use Mediagone\Symfony\EasyApi\EasyApi; use Mediagone\Symfony\EasyApi\Payloads\ApiPayload; use Mediagone\Symfony\EasyApi\Payloads\ApiPayload200Success; use Mediagone\Symfony\EasyApi\Request\ApiPagination; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; #[Route('/api/things/{requestedPage}', name:'api_things_list')] public function __invoke(int $requestedPage = 1, ThingRepository $thingRepository): Response { return $easyApi->response(static function () use ($requestedPage, $thingRepository) : ApiPayload { // Count the total number of Things in the db $thingsCount = $thingRepository->countAll(); // Create a pagination object $pagination = ApiPagination::create($requestedPage, 5, $thingsCount); // Query the page's results $things = $thingRepository->findAllPaginated($pagination); return ApiPayload200Success::createWithArrayResult($things, $pagination); } }
假设您的数据库中有 93 行,您正在请求第 2 页的 5 个结果,您将收到以下 JSON 响应
{ "success": true, "status": "ok", "statusCode": 200, "payload": { "results": [ { "id": 6, "name": "6th thing" }, { "id": 7, "name": "7th thing" }, { "id": 8, "name": "8th thing" }, { "id": 9, "name": "9th thing" }, { "id": 10, "name": "10th thing" } ], "resultsCount": 5, "resultsCountTotal": 93, "page": 2, "pageCount": 19 } }
许可
Symfony EasyAPI 使用 MIT 许可证。请参阅 LICENSE 文件。