我正在使用 Laravel 开发一个项目,在渲染函数内的
Exceptions\Handler.php
中捕获了异常,如下所示:
public function render($request, Exception $e){
switch(get_class($e)){
case SOME_EXCEPTION::class:
do something..
...
...
default:
do something..
}
如您所见,问题是在很多情况下代码变得丑陋且混乱
如何解决这个问题?
如果您的自定义异常扩展了一个公共接口,您可以只检查该接口,然后调用合约方法。
if ($e instanceof CustomExceptionInterface) {
return $e->contractMethod();
}
好的,找到了让它看起来更好的方法。 如果有人想改进 Laravel 中的异常处理程序,请遵循以下步骤:
在 app/providers 下创建新的服务提供者,我们将其命名为 ExceptionServiceProvider.php
class ExceptionServiceProvider extends ServiceProvider {
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton(ExceptionFactory::class);
}
public function boot(ExceptionFactory $factory){
$factory->addException(UnauthorizedException::class, JsonResponse::HTTP_NOT_ACCEPTABLE);
$factory->addException(ConditionException::class, JsonResponse::HTTP_NOT_ACCEPTABLE, "Some Fixed Error Message");
}
}
在项目中的某个位置创建
ExceptionFactory
类,其中包含 addException()
方法以及代码和消息的 getter
class ExceptionFactory{
private $exceptionsMap = [];
private $selectedException;
public function addException($exception, $code, $customMessage = null) {
$this->exceptionsMap[$exception] = [$code, $customMessage];
}
public function getException($exception){
if(isset($this->exceptionsMap[$exception])){
return $this->exceptionsMap[$exception];
}
return null;
}
public function setException($exception){
$this->selectedException = $exception;
}
public function getCode(){
return $this->selectedException[0];
}
public function getCustomMessage(){
return $this->selectedException[1];
}
}
然后剩下要做的就是在
Exceptions/handler.php
内
在渲染函数中:
private $exceptionFactory;
public function __construct(LoggerInterface $log, ExceptionFactory $exceptionFactory){
parent::__construct($log);
$this->exceptionFactory = $exceptionFactory;
}
public function render($request, Exception $e){
$error = new \stdClass();
$customException = $this->exceptionFactory->getException(get_class($e));
if(isset($customException)){
$this->exceptionFactory->setException($customException);
$error->code = $this->exceptionFactory->getCode();
$error->message = $e->getMessage();
$customMessage = $this->exceptionFactory->getCustomMessage();
if(isset($customMessage)){
$error->message = $customMessage;
}
}
return new JsonResponse($error, $error->code);
}
}
最后要记住的是将
ServiceProvider
放在应用程序设置中的 config/app.php
下,只需添加:
\App\Providers\ExceptionServiceProvider::class
我希望你会像我一样发现这很有用。
<?php
namespace App\Exceptions;
use Throwable;
use Illuminate\Support\Str;
use App\Traits\ApiResponser;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Exceptions\HttpResponseException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Saifur\FileManager\app\Facades\Helpers\SFMCommonFacade;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
class Handler extends ExceptionHandler
{
use ApiResponser;
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
public function report(Throwable $exception)
{
parent::report($exception);
}
protected function unauthenticated($request, AuthenticationException $exception)
{
return $this->set_response(null,401,'error',['Unauthenticated.']);
}
public function render($request, Throwable $e)
{
if (\config('app.debug')) // debug mode => true error details is displayed
{
if (Str::startsWith(request()->getRequestUri(), '/log-viewer')) // for web + log viewer + debug mode route handler
{
return redirect('log-viewer/login');
}
return parent::render($request, $e); // for web(except: /log-viewer) + api + debug mode route handler
}
// Processing the status code, exception ($e)
$status = Response::HTTP_INTERNAL_SERVER_ERROR;
[$status, $e, $message] = SFMCommonFacade::sfm_custom_exceptions($e); // Saifur Package Exceptions
if ($e instanceof HttpResponseException)
{
$status = Response::HTTP_INTERNAL_SERVER_ERROR;
}
elseif ($e instanceof MethodNotAllowedHttpException)
{
$status = Response::HTTP_METHOD_NOT_ALLOWED;
$e = new MethodNotAllowedHttpException([], ('Method not allowed!'), $e);
}
elseif ($e instanceof NotFoundHttpException)
{
$status = Response::HTTP_NOT_FOUND;
$e = new NotFoundHttpException(('Not found!'));
}
elseif ($e instanceof AuthorizationException)
{
$status = Response::HTTP_FORBIDDEN;
$e = new AuthorizationException(('Forbidden request! You do not have the required permission to access.'), $status);
}
elseif ($e instanceof \Dotenv\Exception\ValidationException && $e->getResponse())
{
$status = Response::HTTP_BAD_REQUEST;
$e = new \Dotenv\Exception\ValidationException(('Bad Request!'), $status, $e);
}
elseif ($e instanceof AuthenticationException)
{
$status = 401;
$e = new HttpException($status, 'Unauthenticated!');
}
elseif ($e instanceof ThrottleRequestsException)
{
$retry_after = $e->getHeaders()['Retry-After'] ?? 0;
$status = 429;
$e = new HttpException($status, 'Too many requests! Try after '.$retry_after.' seconds!');
}
// Handle OAuth2 Server Exception
elseif ($e instanceof OAuthServerException) {
// Customize the error message based on the exception's properties
$message = 'Access Denied: ' . $e->getMessage();
$status = 403;
$e = new HttpException($status, $message);
}
elseif ($e)
{
$e = new HttpException($status, $message ?? 'Internal Server Error!');
}
// Finally return the response
if (Str::startsWith(request()->getRequestUri(), '/log-viewer') && $status == 401) // for web + log viewer route handler
{
return redirect('log-viewer/login');
}
else if (Str::startsWith(request()->getRequestUri(), '/api')) // for api route handler
{
return $this->set_response(null, $status, 'error', [$e->getMessage()]);
}
else{ // for web route handler
return parent::render($request, $e);
}
}
}