提问人:Arkaik 提问时间:9/7/2023 最后编辑:Brian Tompsett - 汤莱恩Arkaik 更新时间:9/11/2023 访问量:101
处理 laravel 中间件中的异常和致命错误
Handle exceptions and fatal errors into laravel middleware
问:
我目前正在使用 Laravel 7.30.6 开发一个 API,如果出现任何问题,我希望能够处理所有错误并在所有 API 路由上返回带有 HTTP 代码 500 的内部服务器错误,但我想为非 API 相关请求保留默认错误处理程序 ()。app/Exceptions/Handler.php
为此,我创建了一个新的中间件HandleErrors
<?php
// app\Http\Middleware\HandleErrors.php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Response;
use Closure;
use Log;
class HandleErrors
{
public function handle(Request $request, Closure $next)
{
try
{
return $next($request);
}
catch (\Throwable $th)
{
Log::error($th);
return Response::json(array("message"=>"Internal server error"), 500);
}
}
}
我注册了中间件
<?php
// app\Http\Kernel.php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
[...]
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
[...]
'handle-errors' => \App\Http\Middleware\HandleErrors::class,
];
}
我使用这个中间件创建了一个新路由,称为测试控制器
<?php
// routes/api.php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
// */
Route::middleware(["handle-errors"])->group(function () {
Route::get("/v1/test", "API\\v1\TestController@test");
});
在测试控制器中,我自愿生成错误,例如除以零
<?php
// app/Http/Controllers/API/v1/TestController.php
namespace App\Http\Controllers\API\v1;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Response;
use Log;
class TestController extends Controller {
public function test(Request $request)
{
//$a = 10 / 0;
return Response::json(array("message"=>"SUCCESS"), 200);
}
}
当注释除以零时,我确实收到了“成功”的响应。但是,在取消注释时,中间件不会处理异常,并且我收到错误堆栈作为响应。
[...]
<!--
ErrorException: Division by zero in file /var/www/html/my_project/app/Http/Controllers/API/v1/TestController.php on line 15
#0 /var/www/html/my_project/app/Http/Controllers/API/v1/TestController.php(15): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError()
#1 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\Http\Controllers\API\v1\TestController->test()
#2 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\Routing\Controller->callAction()
#3 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Route.php(239): Illuminate\Routing\ControllerDispatcher->dispatch()
#4 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Route.php(196): Illuminate\Routing\Route->runController()
#5 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(685): Illuminate\Routing\Route->run()
#6 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\Routing\Router->Illuminate\Routing\{closure}()
#7 /var/www/html/my_project/app/Http/Middleware/HandleErrors.php(16): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#8 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): App\Http\Middleware\HandleErrors->handle()
#9 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#10 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Routing\Middleware\SubstituteBindings->handle()
#11 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(59): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#12 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Routing\Middleware\ThrottleRequests->handle()
#13 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#14 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(687): Illuminate\Pipeline\Pipeline->then()
#15 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\Routing\Router->runRouteWithinStack()
#16 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(628): Illuminate\Routing\Router->runRoute()
#17 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(617): Illuminate\Routing\Router->dispatchToRoute()
#18 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(165): Illuminate\Routing\Router->dispatch()
#19 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http\{closure}()
#20 /var/www/html/my_project/app/Http/Middleware/corsMiddleware.php(16): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#21 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): App\Http\Middleware\corsMiddleware->handle()
#22 /var/www/html/my_project/vendor/pragmarx/tracker/src/Vendor/Laravel/Middlewares/Tracker.php(24): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#23 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): PragmaRX\Tracker\Vendor\Laravel\Middlewares\Tracker->handle()
#24 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#25 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
#26 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#27 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
#28 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#29 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Foundation\Http\Middleware\ValidatePostSize->handle()
#30 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(63): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#31 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle()
#32 /var/www/html/my_project/vendor/fruitcake/laravel-cors/src/HandleCors.php(37): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#33 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\Cors\HandleCors->handle()
#34 /var/www/html/my_project/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#35 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\Proxy\TrustProxies->handle()
#36 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#37 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(140): Illuminate\Pipeline\Pipeline->then()
#38 /var/www/html/my_project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(109): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter()
#39 /var/www/html/my_project/public/index.php(55): Illuminate\Foundation\Http\Kernel->handle()
#40 {main}
-->
</body>
</html>
如果我用 try/catch 块将函数代码括起来,我确实会按预期收到 HTTP 错误 500。test
public function test(Request $request)
{
try
{
$a = 10 / 0;
return Response::json(array("message"=>"SUCCESS"), 200);
}
catch (\Throwable $th)
{
Log::error($th);
return Response::json(array("message"=>"Internal server error"), 500);
}
}
我想避免将我所有的 API 函数都包含在 try/catch 块中,并直接在中间件中处理异常。
我还意识到有些错误没有得到正确处理,例如,如果我创建了语法错误,则该错误在 catch 块中没有得到正确处理
所以我的问题如下:
- 为什么异常在控制器中被正确处理,而没有被正确处理到中间件中?
- 如何处理语法错误(以及所有可能的异常和错误)并返回带有 HTTP 代码 500 的响应?
答:
让我给你一个更好的主意。
您可以创建一个新类来传递给 Laravel,而不是它自己的类。Handler
Handler
我们称这个新类为,它就位于 旁边。(请注意,扩展了 Laravel 的类。ApiHandler
App/Exceptions/Handler.php
ApiHandler
Handler
你可以在类中拥有这样的逻辑:ApiHandler
<?php
namespace App\Exceptions;
use \Throwable;
use Illuminate\Http\JsonResponse;
class ApiHandler extends Handler
{
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $e
* @return \Symfony\Component\HttpFoundation\Response
*/
public function render($request, Throwable $e)
{
return $this->shouldReturnJson($request, $e) ?
$this->prepareJsonResponse($request, $e) :
$this->prepareResponse($request, $e);
}
/**
* Prepare a JSON response for the given exception.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $e
* @return \Illuminate\Http\JsonResponse
*/
protected function prepareJsonResponse($request, Throwable $e)
{
$data = $this->convertExceptionToArray($e);
return new JsonResponse(
$this->convertExceptionToArray($e),
$data['status'] ?? 500,
$this->isHttpException($e) ? $e->getHeaders() : [],
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
);
}
/**
* Convert the given exception to an array.
*
* @param \Throwable $e
* @return array
*/
protected function convertExceptionToArray(Throwable $e)
{
$response = [
'code' => $this->isHttpException($e) ? $e->getCode() : 500,
'message' => $e->getMessage() ?? 'Internal Server Error',
];
if (env('APP_DEBUG')) {
$response ['file'] = $e->getFile();
$response ['line'] = $e->getLine();
$response ['trace'] = $e->getTrace();
}
return $response;
}
}
现在你必须告诉 Laravel 使用这个类,而不是它自己的类。
转到并注册。AppServiceProvider
ApiHandler
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// Registering api handler instead of
// Laravel's built-in one. (Comment if want to revert)
$this->app->bind(
\Illuminate\Contracts\Debug\ExceptionHandler::class,
\App\Exceptions\ApiHandler::class
);
}
现在,每当您的应用程序捕获异常时,它都会使用此类并为您提供所需的响应结构。请注意,我添加了一些细节,以便在调试系统时为您和您的同事创造更好的开发人员体验。
干杯。
上一个:纠正不一致的返回点
评论
app/Exceptions/Handler.php