phpro / api-problem
RFC7807 问题详情实现
1.7.0
2023-11-24 08:41 UTC
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.13
- phpro/grumphp-shim: ^2.3
- phpspec/phpspec: ^7.2
- sebastian/comparator: ^4.0
- symfony/validator: ^5.4 || ^6.0 || ^7.0
Suggests
- symfony/validator: If you want to use the ValidationApiProblem
README
Api Problem
本包提供了RFC7807 问题详情实现。它可以集成到代码的任何地方,并应生成适用于 HTTP API 的一般错误响应格式。
本包只提供通用接口、异常类和一些内置的 API 问题消息。由于异常处理由框架负责,以下列出了已知的框架集成列表
- Symfony
^4.1
: ApiProblemBundle
安装
composer require phpro/api-problem
使用
本包提供创建 ApiProblem 值对象的通用接口。
use Phpro\ApiProblem\Exception; throw new ApiProblemException( new HttpApiProblem(418, ['detail' => 'Did you know 4,000 people are injured by teapots every year?!']) );
内置问题
-
通用问题
-
Symfony 集成问题
-
HTTP 问题
ExceptionApiProblem
可调试:在调试环境中才会添加 exception
部分!
use Phpro\ApiProblem\Http\ExceptionApiProblem; new ExceptionApiProblem(new \Exception('message', 500));
{ "status": 500, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Internal Server Error", "detail": "message", "exception": { "message": "message", "type": "RuntimeException", "code": 500, "line": 23, "file": "exception.php", "trace": [ "#0 [internal function]: ...", "#1 [internal function]: ...", "#3 [internal function]: ...", "..." ], "previous": [ { "message": "previous", "type": "InvalidArgumentException", "code": 0, "line": 20, "file": "exception.php", "trace": [ "#0 [internal function]: ...", "#1 [internal function]: ...", "#3 [internal function]: ...", "..." ] } ] } }
HttpApiProblem
use Phpro\ApiProblem\Http\HttpApiProblem; new HttpApiProblem(404, ['detail' => 'The book could not be found.']);
{ "status": 404, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Not found", "detail": "The book could not be found." }
验证ApiProblem
composer require symfony/validator:^4.1
use Phpro\ApiProblem\Http\ValidationApiProblem; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; new ValidationApiProblem(new ConstraintViolationList([ new ConstraintViolation('Invalid email', '', [], '', 'email', '', null, '8615ecd9-afcb-479a-9c78-8bcfe260cf2a'), ]));
{ "status": 400, "type": "https:\/\/symfony.com\/errors\/validation", "title": "Validation Failed", "detail": "email: Invalid Email", "violations": [ { "propertyPath": "email", "title": "Invalid email", "type": "urn:uuid:8615ecd9-afcb-479a-9c78-8bcfe260cf2a" } ] }
BadRequestProblem
use Phpro\ApiProblem\Http\BadRequestProblem; new BadRequestProblem('Bad request. Bad!.');
{ "status": 400, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Bad Request", "detail": "Bad request. Bad!" }
UnauthorizedProblem
use Phpro\ApiProblem\Http\UnauthorizedProblem; new UnauthorizedProblem('You are not authorized to access X.');
{ "status": 401, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Unauthorized", "detail": "You are not authenticated. Please login." }
ForbiddenProblem
use Phpro\ApiProblem\Http\ForbiddenProblem; new ForbiddenProblem('Not authorized to access gold.');
{ "status": 403, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Forbidden", "detail": "Not authorized to access gold." }
NotFoundProblem
use Phpro\ApiProblem\Http\NotFoundProblem; new NotFoundProblem('The book with ID 20 could not be found.');
{ "status": 404, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Not found", "detail": "The book with ID 20 could not be found." }
MethodNotAllowedProblem
use Phpro\ApiProblem\Http\MethodNotAllowedProblem; new MethodNotAllowedProblem('Only POST and GET allowed.');
{ "status": 405, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Method Not Allowed", "detail": "Only POST and GET allowed." }
ConflictProblem
use Phpro\ApiProblem\Http\ConflictProblem; new ConflictProblem('Duplicated key for book with ID 20.');
{ "status": 409, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Conflict", "detail": "Duplicated key for book with ID 20." }
PreconditionFailedProblem
use Phpro\ApiProblem\Http\PreconditionFailedProblem; new PreconditionFailedProblem('Incorrect entity tag provided.');
{ "status": 412, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Precondition Failed", "detail": "Incorrect entity tag provided." }
UnsupportedMediaTypeProblem
use Phpro\ApiProblem\Http\UnsupportedMediaTypeProblem; new UnsupportedMediaTypeProblem('Please provide valid JSON.');
{ "status": 415, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Unsupported Media Type", "detail": "Please provide valid JSON." }
IAmATeapotProblem
use Phpro\ApiProblem\Http\IAmATeapotProblem; new IAmATeapotProblem('More tea please.');
{ "status": 418, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "I'm a teapot", "detail": "More tea please." }
UnprocessableEntityProblem
use Phpro\ApiProblem\Http\UnprocessableEntityProblem; new UnprocessableEntityProblem('Unable to process the contained instructions.');
{ "status": 422, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Unprocessable Entity", "detail": "Unable to process the contained instructions." }
LockedProblem
use Phpro\ApiProblem\Http\LockedProblem; new LockedProblem('This door is locked.');
{ "status": 423, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Locked", "detail": "This door is locked." }
PreconditionRequiredProblem
use Phpro\ApiProblem\Http\PreconditionRequiredProblem; new PreconditionRequiredProblem('If-match header is required.');
{ "status": 428, "type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html", "title": "Precondition Required", "detail": "If-match header is required." }
创建自己的问题
创建问题听起来很可怕,对吧?由于 RFC 很宽松,我们使接口尽可能简单
use Phpro\ApiProblem\ApiProblemInterface; class MyProblem implements ApiProblemInterface { public function toArray(): array { return [ 'type' => 'about:blank', 'status' => '99', 'title' => 'Got 99 problems but a glitch aint one!', ]; } }
许多问题将在 HTTP 上下文中检测到。因此,我们还提供了一个基类 HttpApiProblem
。这个类将根据 HTTP 状态码自动填充类型和标题部分。您唯一需要做的是添加一些附加数据
use Phpro\ApiProblem\Http\HttpApiProblem; class MyProblem extends HttpApiProblem { public function __construct(string $details) { parent::__construct(500, ['details' => $details]); } }
如果您希望在调试环境中记录附加信息,可以实现一个额外的 DebuggableApiProblemInterface
use Phpro\ApiProblem\DebuggableApiProblemInterface; class MyProblem implements DebuggableApiProblemInterface { public function toArray(): array { return [ 'type' => 'about:blank', 'status' => '99', 'title' => 'Got 99 problems but a glitch ain\'t one!', ]; } public function toDebuggableArray(): array { return array_merge( $this->toArray(), [ 'situation' => 'If you are having code problems, I feel bad for you son', ] ); } }
关于
提交错误和功能请求
错误和功能请求在 GitHub 上跟踪。在贡献代码之前,请查看我们的规则。
许可证
api-problem 在 MIT 许可证 下发布。