gsousadev/laravel-problem-detail-exceptions-lib

用于标准化使用 'problem details' 格式的 exceptions 的包

1.2.2 2024-02-09 18:37 UTC

README

Versão Licença

该项目旨在提供一个Laravel库,允许通过遵循RFC problem details的概念,以简单快捷的方式实现exceptions的标准

安装

可以使用Composer安装此包

composer require gsousadev/laravel-problem-detail-exceptions-lib

安装后,需要发布包的配置文件。使用以下命令来完成此操作

php artisan vendor:publish --tag=problem-detail-exceptions

发布包后,应在以下路径创建一个用于项目的配置文件:/config/problem-detail-exceptions.php

<?php

use Gsousadev\LaravelProblemDetailExceptions\Enums\ExceptionsFieldsEnum;

return [
    'app_name' => env('PROBLEM_DETAIL_EXCEPTION_APP_NAME', 'APP'),
    'log_throw' => env('PROBLEM_DETAIL_EXCEPTION_GENERATE_LOGS', true),
    'available_fields_list' => [
        ExceptionsFieldsEnum::TYPE,
        ExceptionsFieldsEnum::TITLE,
        ExceptionsFieldsEnum::STATUS,
        ExceptionsFieldsEnum::DETAIL,
        ExceptionsFieldsEnum::INTERNAL_CODE,
        ExceptionsFieldsEnum::MESSAGE,
        ExceptionsFieldsEnum::USER_MESSAGE,
        ExceptionsFieldsEnum::USER_TITLE,
        ExceptionsFieldsEnum::LOCATION,
        ExceptionsFieldsEnum::TRACE_ID,
        ExceptionsFieldsEnum::PREVIOUS_MESSAGE,
        ExceptionsFieldsEnum::PREVIOUS_CODE,
        ExceptionsFieldsEnum::PREVIOUS_TYPE,
        ExceptionsFieldsEnum::PREVIOUS_LOCATION

    ],
    'renderable_fields_list' => [
        ExceptionsFieldsEnum::TITLE,
        ExceptionsFieldsEnum::STATUS,
        ExceptionsFieldsEnum::USER_MESSAGE,
        ExceptionsFieldsEnum::USER_TITLE,
    ],

];

配置

此包允许一些自定义配置。为此,应使用文件 problem-detail-exceptions.php

为了在流程中保持数据一致性,非常重要地实现两个环境变量

  • PROBLEM_DETAIL_EXCEPTION_APP_NAME : 指示可能出现在日志中的项目或应用程序名称。
  • PROBLEM_DETAIL_EXCEPTION_GENERATE_LOGS : 此变量允许在发生错误时开启或关闭要发布的日志。

示例

PROBLEM_DETAIL_EXCEPTION_APP_NAME=nome_do_aplicativo
PROBLEM_DETAIL_EXCEPTION_GENERATE_LOGS=true

此外,可以在配置文件中配置哪些字段应考虑为API exceptions的字段

存在两种可能的配置:在任何上下文中应使用的字段 fields 以及在错误响应中应显示的字段 renderable_fields

注意:不推荐在HTTP响应中使用所有可用字段,因为某些字段可能包含应安全存储或不应向任何人公开的信息。

所有字段都必须根据也位于项目中的Enum类进行注册。

以下Enum中包含可用字段

<?php

namespace Gsousadev\LaravelProblemDetailExceptions\Enums;

enum ExceptionsFieldsEnum: string
{
    case TYPE = 'type';
    case TITLE = 'title';
    case STATUS = 'status';
    case DETAIL = 'detail';
    case INTERNAL_CODE = 'internal_code';
    case MESSAGE = 'message';
    case USER_MESSAGE = 'user_message';
    case USER_TITLE = 'user_title';
    case LOCATION = 'location';
    case TRACE_ID = 'trace_id';
    case PREVIOUS_MESSAGE = 'previous_message';
    case PREVIOUS_TYPE = 'previous_type';
    case PREVIOUS_CODE = 'previous_code';
    case PREVIOUS_LOCATION = 'previous_location';
}

创建自定义Exceptions

为了使用此包,建议创建扩展自 Gsousadev\LaravelProblemDetailExceptions\Exceptions\ProblemDetailException 类的异常,如下所示示例类

...

use Gsousadev\LaravelProblemDetailExceptions\Exceptions\ProblemDetailException;

class ExampleException extends ProblemDetailException

然后,我们必须在构造函数中对父类构造函数进行调用,传递创建异常所需的所有参数。

...

    public function __construct(?\Throwable $previous = null)
    {
        parent::__construct(
            title:        'Titulo curto para erro. Deve ser imutável',
            detail:       'Descrição mais detalhada do erro podendo conter variaveis dinâmicas.' .
                          'Pode ser mutável a cada lançamento dependendo do contexto',
            userTitle:    'Titulo amigavel para usuário final que pode ver o erro',
            userMessage:  'Detalhamento amigavel para usuário que pode ver o erro',
            httpStatus:  500,
            internalCode: 'SHRD0001',
            previous:     $previous
        );
    }
...

最终结果应如下所示

<?php

declare(strict_types=1);

namespace App\Exceptions;

use Gsousadev\LaravelProblemDetailExceptions\Exceptions\ProblemDetailException;

class ExampleException extends ProblemDetailException
{
    public function __construct(?\Throwable $previous = null)
    {
        parent::__construct(
            title:        'Titulo curto para erro. Deve ser imutável',
            detail:       'Descrição mais detalhada do erro podendo conter variaveis dinâmicas.' .
                          'Pode ser mutável a cada lançamento dependendo do contexto',
            userTitle:    'Titulo amigavel para usuário final que pode ver o erro',
            userMessage:  'Detalhamento amigavel para usuário que pode ver o erro',
            httpStatus:  500,
            internalCode: 'SHRD0001',
            previous:     $previous
        );
    }

}

这样,就可以获得一个可读性良好、完整且具有简单直接调用方式的异常。

try {
...
} catch(\Exception $exception){
    throw new ExampleException($exception);
}

异常日志

还可以通过使用 $logThrow 选项配置每个异常的日志。此选项允许我们为每个异常配置是否生成日志,而不管整体配置如何。如果省略,则采用整体日志配置。

示例

...

class ExampleException extends ProblemDetailException
{
    protected ?bool $logThrow = true

    public function __construct(?\Throwable $previous = null)
    
...

自动标准化Exceptions

存在一个类可以与Laravel的Exceptions Handler一起使用,允许将所有抛出并由handler处理的exceptions转换为并标准化为'Problem Detail'格式。这允许应用程序快速将不同格式的exceptions的输出标准化为日志和HTTP请求。通常,Laravel的handler文件位于此处 App\Exceptions\Handler.php。可以按照以下示例实现此类

<?php

namespace App\Exceptions;

use Gsousadev\LaravelProblemDetailExceptions\Exceptions\ProblemDetailException;
use Gsousadev\LaravelProblemDetailExceptions\Exceptions\UnexpectedException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
    public function register()
    {
        $this->renderable(function (Throwable $e) {
            if (!$e instanceof ProblemDetailException) {
                throw new UnexpectedException($e);
            }
        });
    }

    public function report(Throwable $e)
    {
        if ($e instanceof ProblemDetailException) {
            parent::report($e);
        }
    }
}

贡献

该项目处于建设阶段,改进建议对项目成长非常重要。为了提出改进建议,请使用GitHub的Issue功能。

许可证

MIT