refactor: 重构语言协商中间件,简化语言解析逻辑并增强异常处理,更新相关配置以支持多环境开发

This commit is contained in:
2026-05-08 17:40:09 +08:00
parent 85e57782cc
commit e478597d13
11 changed files with 295 additions and 54 deletions

View File

@@ -9,9 +9,21 @@
|--------------------------------------------------------------------------
*/
use App\Http\Middleware\EnsureAdminApi;
use App\Http\Middleware\EnsurePlayerApi;
use App\Http\Middleware\NegotiateLotteryLocale;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use App\Support\LotteryLocale;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
@@ -24,15 +36,115 @@ return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
// 多语言:必须在其他 api 中间件之前执行,以便鉴权失败时也能按语言返回 msg
$middleware->api(prepend: [
\App\Http\Middleware\NegotiateLotteryLocale::class,
NegotiateLotteryLocale::class,
]);
$middleware->alias([
// 玩家端需登录路由使用;解析 Bearer → Player
'lottery.player' => \App\Http\Middleware\EnsurePlayerApi::class,
'lottery.player' => EnsurePlayerApi::class,
// 后台 API 预留Sanctum / RBAC
'lottery.admin' => \App\Http\Middleware\EnsureAdminApi::class,
'lottery.admin' => EnsureAdminApi::class,
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
/**
* 统一 JSON {@see ApiResponse} 一致,供三端消费;多语言与 {@see LotteryLocale} 对齐。
* 覆盖:校验失败、模型/路由 404、限流、未处理异常(生产不泄露堆栈文案)。
*/
$locale = static function (Request $request): string {
return (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request));
};
$exceptions->render(function (ValidationException $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
return ApiResponse::error(
trans('api.validation_failed', [], $locale($request)),
ErrorCode::ValidationFailed->value,
['errors' => $e->errors()],
422,
);
});
$exceptions->render(function (ModelNotFoundException $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
return ApiResponse::error(
trans('api.not_found', [], $locale($request)),
ErrorCode::NotFound->value,
null,
404,
);
});
$exceptions->render(function (NotFoundHttpException $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
return ApiResponse::error(
trans('api.not_found', [], $locale($request)),
ErrorCode::NotFound->value,
null,
404,
);
});
$exceptions->render(function (TooManyRequestsHttpException $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
return ApiResponse::error(
trans('api.too_many_requests', [], $locale($request)),
ErrorCode::TooManyRequests->value,
null,
429,
);
});
$exceptions->render(function (HttpException $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
if ($e instanceof NotFoundHttpException || $e instanceof TooManyRequestsHttpException) {
return null;
}
$status = $e->getStatusCode();
$msg = $e->getMessage();
if ($msg === '') {
$msg = trans('api.client_error', [], $locale($request));
}
$code = $status >= 500 ? ErrorCode::InternalError->value : ErrorCode::ClientHttpError->value;
return ApiResponse::error($msg, $code, null, $status);
});
$exceptions->render(function (Throwable $e, Request $request) use ($locale) {
if (! $request->is('api/*')) {
return null;
}
if ($e instanceof ValidationException
|| $e instanceof ModelNotFoundException
|| $e instanceof NotFoundHttpException
|| $e instanceof TooManyRequestsHttpException
|| $e instanceof HttpException
) {
return null;
}
$showDetails = (bool) config('app.debug');
$msg = $showDetails ? $e->getMessage() : trans('api.server_error', [], $locale($request));
return ApiResponse::error(
$msg !== '' ? $msg : trans('api.server_error', [], $locale($request)),
ErrorCode::InternalError->value,
$showDetails ? ['exception' => $e::class] : null,
500,
);
});
})->create();