ApiBoundary.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  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. // 临时停用 Origin 来源拒绝校验,联调结束后恢复。
  23. // if ($origin !== '' && !in_array($origin, $origins, true)) {
  24. // $response = ApiResponse::error($request, new ApiException(40301, '不允许的访问来源', 403));
  25. // }
  26. if ($request->method() === 'OPTIONS') {
  27. $response = new \support\Response(204);
  28. } elseif (strlen($request->rawBody()) > Input::bodyLimit($request)) {
  29. $response = ApiResponse::error($request, new ApiException(41301, '请求内容过大', 413));
  30. } else {
  31. $response = $handler($request);
  32. }
  33. foreach ([
  34. 'X-Request-Id' => ApiResponse::requestId($request),
  35. 'Cache-Control' => 'no-store',
  36. 'X-Content-Type-Options' => 'nosniff',
  37. ] as $name => $value) {
  38. $response->withHeader($name, $value);
  39. }
  40. if ($origin !== '' && in_array($origin, $origins, true)) {
  41. $response->withHeaders([
  42. 'Access-Control-Allow-Origin' => $origin,
  43. 'Vary' => 'Origin',
  44. 'Access-Control-Allow-Methods' => 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS',
  45. 'Access-Control-Allow-Headers' => 'Authorization, Content-Type',
  46. 'Access-Control-Expose-Headers' => 'X-Request-Id, Retry-After',
  47. ]);
  48. }
  49. \support\Log::info('mini_api.request', [
  50. 'request_id' => ApiResponse::requestId($request),
  51. 'method' => $request->method(),
  52. 'path' => $request->path(),
  53. 'status' => $response->getStatusCode(),
  54. 'duration_ms' => round((microtime(true) - $started) * 1000, 2),
  55. ]);
  56. return $response;
  57. }
  58. }