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 许可证 下发布。