ApiResponse.php 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\miniapi\http;
  4. use Webman\Http\Request;
  5. use support\Response;
  6. use app\miniapi\exception\ApiException;
  7. final class ApiResponse
  8. {
  9. public static function owns(Request $request): bool
  10. {
  11. return $request->path() === '/api/mini' || str_starts_with($request->path(), '/api/mini/');
  12. }
  13. public static function requestId(Request $request): string
  14. {
  15. return $request->miniRequestId ??= bin2hex(random_bytes(16));
  16. }
  17. public static function success(Request $request, mixed $data): Response
  18. {
  19. return self::make($request, 200, 0, 'ok', $data);
  20. }
  21. public static function error(Request $request, ApiException $error): Response
  22. {
  23. $response = self::make(
  24. $request,
  25. $error->status,
  26. $error->getCode(),
  27. $error->getMessage(),
  28. $error->fields ? ['fields' => $error->fields] : null
  29. );
  30. if (in_array($error->status, [429, 503], true) && isset($error->fields['retry_after'])) {
  31. $response->withHeader('Retry-After', (string) max(1, (int) $error->fields['retry_after']));
  32. }
  33. return $response;
  34. }
  35. private static function make(Request $request, int $status, int $code, string $message, mixed $data): Response
  36. {
  37. $id = self::requestId($request);
  38. return new Response(
  39. $status,
  40. [
  41. 'Content-Type' => 'application/json; charset=utf-8',
  42. 'Cache-Control' => 'no-store',
  43. 'X-Request-Id' => $id,
  44. 'X-Content-Type-Options' => 'nosniff',
  45. ],
  46. json_encode([
  47. 'code' => $code,
  48. 'message' => $message,
  49. 'data' => $data,
  50. 'request_id' => $id,
  51. ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_THROW_ON_ERROR)
  52. );
  53. }
  54. }