You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel服务提供者数据库请求偶发500错误(SQLSTATE[HY000][1045])

问题描述

尝试从数据库加载应用配置:缓存为空时查询数据库,否则从配置文件加载。但在AppServiceProvider的boot方法中执行数据库请求时,偶发500错误,错误日志显示数据库连接权限问题:

[2024-08-03 09:40:00] production.ERROR: SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost' (using password: YES) (Connection: mysql, SQL: select * from `settings` where `settings`.`id` = 1 limit 1) {"exception":"[object] (Illuminate\\Database\\QueryException(code: 1045): SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost' (using password: YES) (Connection: mysql, SQL: select * from `settings` where `settings`.`id` = 1 limit 1) at D:\\Web\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Connection.php:829)
[stacktrace]
#0 

相关代码(AppServiceProvider.php):

public function boot(): void
{
    Schema::defaultStringLength(191);
    Vite::useScriptTagAttributes(['async' => true]);
    Blade::anonymousComponentPath(__DIR__.'/../../resources/views/admin/layout/components', 'admin');
    Blade::anonymousComponentPath(__DIR__.'/../../resources/views/template/layout/components', 'template');

    $namesVariables = [
        'name',
        'description',
        'lang',
        'timezone',
        'telegram',
        'url',
        'admin_url',
    ];

    foreach($namesVariables as $variable)
    {
        ${'site_'.$variable} = Cache::get('site_'.$variable, '');
    }

    if (empty($site_name) && empty($site_description)
                          && empty($site_lang)
                          && empty($site_timezone)
                          && empty($site_telegram)
                          && empty($site_url)
                          && empty($site_admin_url))
    {
        $settings = Setting::find(1);

        if (!empty($settings))
        {
            foreach($namesVariables as $variable)
            {
                ${'site_'.$variable} = $settings['site_'.$variable];
                Cache::put('site_'.$variable, ${'site_'.$variable},
                       now()->addMinutes($this->cacheNum));
            }
        } else {
            $site_name = env('APP_NAME');
            $site_description = env('APP_DESCRIPTION');
            $site_telegram = env('APP_TELEGRAM');
            $site_url = env('APP_URL');
            $site_admin_url = env('APP_ADMIN_URL');

            Cache::put('site_name', $site_name,
                   now()->addMinutes($this->cacheNum));
            Cache::put('site_description', $site_description,
                   now()->addMinutes($this->cacheNum));
            Cache::put('site_telegram', $site_telegram,
                   now()->addMinutes($this->cacheNum));
            Cache::put('site_url', $site_url,
                   now()->addMinutes($this->cacheNum));
            Cache::put('site_admin_url', $site_admin_url,
                   now()->addMinutes($this->cacheNum));
        }
    }

    Config::set('app.name', $site_name);
    Config::set('app.site_description', $site_description);
    App::setLocale($site_lang);
    Config::set('app.timezone', $site_timezone);
    Config::set('app.site_telegram', $site_telegram);
    Config::set('app.url', $site_url);
    Config::set('app.site_admin_url', $site_admin_url);
}

问题原因

  1. 数据库连接初始化时机:boot方法执行时,Laravel的数据库连接可能尚未完全初始化(比如队列任务、artisan命令启动阶段),此时执行查询会使用框架默认的forge数据库用户,而非自定义配置。
  2. env()函数失效:应用执行config:cache后,env()函数无法读取.env文件内容,导致默认配置加载失败。
  3. 未处理异常:数据库连接失败时没有 fallback逻辑,直接抛出错误导致500。

解决方案

1. 捕获数据库异常,添加 fallback逻辑

修改boot方法中的数据库查询部分,捕获连接异常,失败时直接使用默认配置:

// 替换原有的$settings = Setting::find(1);部分
try {
    // 先验证数据库连接是否可用
    DB::connection()->getPdo();
    $settings = Setting::find(1);
} catch (\Illuminate\Database\QueryException|\PDOException $e) {
    // 连接失败,使用默认配置
    $settings = null;
}

2. 改用config()读取默认配置

在config/app.php中定义自定义配置项,避免直接使用env():

// config/app.php
return [
    // ...原有配置
    'site_description' => env('APP_DESCRIPTION', '默认站点描述'),
    'site_telegram' => env('APP_TELEGRAM', ''),
    'site_admin_url' => env('APP_ADMIN_URL', '/admin'),
];

然后在代码中用config()替代env():

// 替换else分支中的env调用
$site_name = config('app.name');
$site_description = config('app.site_description');
$site_telegram = config('app.site_telegram');
$site_url = config('app.url');
$site_admin_url = config('app.site_admin_url');

3. 延迟配置加载(推荐)

将配置加载逻辑放到路由中间件中,此时数据库连接已完全初始化,避免启动阶段的连接问题:

步骤1:创建中间件LoadSiteConfig

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\App;
use App\Models\Setting;

class LoadSiteConfig
{
    public function handle($request, Closure $next)
    {
        $namesVariables = [
            'name',
            'description',
            'lang',
            'timezone',
            'telegram',
            'url',
            'admin_url',
        ];

        $cacheHasContent = false;
        foreach($namesVariables as $variable)
        {
            ${'site_'.$variable} = Cache::get('site_'.$variable, '');
            if (!empty(${'site_'.$variable})) {
                $cacheHasContent = true;
            }
        }

        if (!$cacheHasContent) {
            try {
                $settings = Setting::find(1);

                if ($settings) {
                    foreach($namesVariables as $variable)
                    {
                        ${'site_'.$variable} = $settings['site_'.$variable];
                        Cache::put('site_'.$variable, ${'site_'.$variable}, now()->addMinutes(60));
                    }
                } else {
                    // 从配置文件读取默认值
                    $site_name = config('app.name');
                    $site_description = config('app.site_description');
                    $site_lang = config('app.locale');
                    $site_timezone = config('app.timezone');
                    $site_telegram = config('app.site_telegram');
                    $site_url = config('app.url');
                    $site_admin_url = config('app.site_admin_url');

                    // 缓存默认配置
                    Cache::put('site_name', $site_name, now()->addMinutes(60));
                    Cache::put('site_description', $site_description, now()->addMinutes(60));
                    Cache::put('site_lang', $site_lang, now()->addMinutes(60));
                    Cache::put('site_timezone', $site_timezone, now()->addMinutes(60));
                    Cache::put('site_telegram', $site_telegram, now()->addMinutes(60));
                    Cache::put('site_url', $site_url, now()->addMinutes(60));
                    Cache::put('site_admin_url', $site_admin_url, now()->addMinutes(60));
                }
            } catch (\Exception $e) {
                // 数据库连接失败,强制使用默认配置
                $site_name = config('app.name');
                $site_description = config('app.site_description');
                $site_lang = config('app.locale');
                $site_timezone = config('app.timezone');
                $site_telegram = config('app.site_telegram');
                $site_url = config('app.url');
                $site_admin_url = config('app.site_admin_url');
            }
        }

        // 覆盖应用配置
        Config::set('app.name', $site_name);
        Config::set('app.site_description', $site_description);
        App::setLocale($site_lang ?? config('app.locale'));
        Config::set('app.timezone', $site_timezone ?? config('app.timezone'));
        Config::set('app.site_telegram', $site_telegram);
        Config::set('app.url', $site_url);
        Config::set('app.site_admin_url', $site_admin_url);

        return $next($request);
    }
}

步骤2:注册中间件

在app/Http/Kernel.php的web中间件组中添加该中间件:

protected $middlewareGroups = [
    'web' => [
        // ...原有中间件
        \App\Http\Middleware\LoadSiteConfig::class,
    ],
];

4. 清除配置缓存

执行以下命令清除错误的配置缓存:

php artisan config:clear
php artisan cache:clear

内容的提问来源于stack exchange,提问作者user26816657

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:09:51