ApiBoundary.php 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\miniapi\middleware;
  4. use Webman\Http\Request;
  5. use Webman\Http\Response;
  6. use Webman\MiddlewareInterface;
  7. use app\miniapi\http\ApiResponse;
  8. use app\miniapi\http\Input;
  9. use app\miniapi\exception\ApiException;
  10. final class ApiBoundary implements MiddlewareInterface
  11. {
  12. public function process(Request $request, callable $handler): Response
  13. {
  14. if (!ApiResponse::owns($request)) {
  15. return $handler($request);
  16. }
  17. $request->miniRequestId = bin2hex(random_bytes(16));
  18. $request->miniPrincipal = null;
  19. $origin = (string) $request->header('origin', '');
  20. $origins = (array) config('mini_api.allowed_origins', []);
  21. $started = microtime(true);
  22. if ($origin !== '' && !in_array($origin, $origins, true)) {
  23. $response = ApiResponse::error($request, new ApiException(40301, '不允许的访问来源', 403));
  24. } elseif ($request->method() === 'OPTIONS') {
  25. $response = new \support\Response(204);
  26. } elseif (strlen($request->rawBody()) > Input::bodyLimit($request)) {
  27. $response = ApiResponse::error($request, new ApiException(41301, '请求内容过大', 413));
  28. } else {
  29. $response = $handler($request);
  30. }
  31. foreach ([
  32. 'X-Request-Id' => ApiResponse::requestId($request),
  33. 'Cache-Control' => 'no-store',
  34. 'X-Content-Type-Options' => 'nosniff',
  35. ] as $name => $value) {
  36. $response->withHeader($name, $value);
  37. }
  38. if ($origin !== '' && in_array($origin, $origins, true)) {
  39. $response->withHeaders([
  40. 'Access-Control-Allow-Origin' => $origin,
  41. 'Vary' => 'Origin',
  42. 'Access-Control-Allow-Methods' => 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS',
  43. 'Access-Control-Allow-Headers' => 'Authorization, Content-Type',
  44. 'Access-Control-Expose-Headers' => 'X-Request-Id, Retry-After',
  45. ]);
  46. }
  47. \support\Log::info('mini_api.request', [
  48. 'request_id' => ApiResponse::requestId($request),
  49. 'method' => $request->method(),
  50. 'path' => $request->path(),
  51. 'status' => $response->getStatusCode(),
  52. 'duration_ms' => round((microtime(true) - $started) * 1000, 2),
  53. ]);
  54. return $response;
  55. }
  56. }