【问题标题】:Custom exception for each invalid input value每个无效输入值的自定义异常
【发布时间】:2020-06-06 22:50:38
【问题描述】:

我有一个后端项目,其中存在自己的“父级到所有异常”,类似这样(我也有从这个异常派生的 InvalidArgumentException):

class CaliException extends \Exception {
    private $caliCode;

    public function __construct(string $message = "", string $caliCode = self::UNDEFINED_CODE,
                                int $code = 0, Throwable $previous = null) {
        parent::__construct($message, $code, $previous);

        $this->caliCode = $caliCode;
    }

    function getCaliCode(): string {
        return $this->caliCode;
    }
}

从上面的代码中可以清楚地看出,自定义类的目的是能够保留字符串代码。为什么我需要字符串代码?因为我有 HTTP API,它的工作方式如下:

  1. 获取 JSON 请求
  2. 做点什么
  3. 使用“状态”字段生成 JSON 输出,该字段包含抛出异常的代码或“200”响应。这些状态码对于客户来说非常有用,他们可以区分不同的问题并做出相应的反应。

所以有问题。上面的系统产生这样的代码:

class UsernameFormatException extends InvalidArgumentException {
    public const USERNAME_FORMAT_UNDEFINED_CODE = "DM:USERCA:0001";

    public function __construct(string $message = "", string $calistoCode = self::USERNAME_FORMAT_UNDEFINED_CODE,
                                int $code = 0, \Throwable $previous = null) {
        parent::__construct($message, $calistoCode, $code, $previous);
    }
}

class PasswordFormatException extends InvalidArgumentException {
    public const PASSWORD_FORMAT_UNDEFINED_CODE = "DM:USERCA:0002";

    public function __construct(string $message = "", string $calistoCode = self::PASSWORD_FORMAT_UNDEFINED_CODE,
                                int $code = 0, \Throwable $previous = null) {
        parent::__construct($message, $calistoCode, $code, $previous);
    }
}

class InvalidLastActivityTimestampException extends InvalidArgumentException {
    public const INVALID_TIMESTAMP = "DM:USERCA:0003";

    public function __construct(string $message = "", string $calistoCode = self::INVALID_TIMESTAMP,
                                int $code = 0, \Throwable $previous = null) {
        parent::__construct($message, $calistoCode, $code, $previous);
    }
}

class InvalidCreationTimestampException extends InvalidArgumentException {
    public const INVALID_TIMESTAMP = "DM:USERCA:0004";

    public function __construct(string $message = "", string $calistoCode = self::INVALID_TIMESTAMP,
                                int $code = 0, \Throwable $previous = null) {
        parent::__construct($message, $calistoCode, $code, $previous);
    }
}

如所见,对于每个无效参数情况,我都会创建新的CaliException-派生异常。我认为它不酷。好吗?如何改进代码?

奖励问题:我读到当这是程序员的错误时我应该抛出\InvalidArgumentException,所以不能捕获异常。但在我的代码中没有\InvalidArgumentException 只有我自己的版本。在最佳实践等方面是否可以接受?以及如何区分是程序员的错误和用户的错误(无效的用户输入)? (毕竟,任何传递给函数的无效值都是相对于该函数无效的输入

【问题讨论】:

  • 如果代码有效,而您只是要求我们对其进行审核,请尝试将其发布到 Code Review
  • "如何区分是程序员的错误和用户的错误" - 如果程序员将用户数据传递给需要特定类型的方法而没有先验证它,恕我直言,这仍然是程序员的错。如果用户传递了无效数据,您应该返回 400 Bad Request(如果是 API)

标签: php exception architecture


【解决方案1】:

从您的描述看来,您实际上是在制作类似 REST API 的东西。或者至少是受该概念启发的东西。

InvalidArgumentException 的例外情况是,有人通过了您的第二或第三手知识。这些异常必须被捕获,而不是在域实体服务层。取而代之的是,您在 .. 好 .. 引导级别捕获它们,在那里您使用所有标头和状态代码呈现“漂亮的错误 json”响应。

至于你原来的问题。是的,有非常具体的例外是可以的。不过,我建议不要将PasswordFormatException 命名为MalformedPassword。基本上,放弃*Exeception 后缀,原因与您没有class UserClass 后缀的原因相同——它毫无价值,PHP 现在有命名空间(自 2005 年以来......我认为)。

这只是一个快速的'n'drunk评论。我什至不确定它是否能回答你的问题。

【讨论】:

  • “有非常具体的例外是可以的”,但这意味着我需要为几乎每个参数创建新的例外,不是吗?
  • @wcobalt 你看错了。您不为“for parameter”做例外,而是为违反域业务规则。
猜你喜欢
  • 2020-03-26
  • 2013-10-11
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2012-01-16
  • 1970-01-01
  • 2016-08-22
  • 2015-05-18
相关资源
最近更新 更多