return 向用户描述错误的最佳方式
Best way to return error description to user
假设我需要在我的系统中注册用户。
商业规则是:
- 电子邮件应该是唯一的(一种身份);
- 名字不能为空。
看来我需要服务。
可能是这样的:
public interface RegistrationService {
bool Register(String email, String name);
}
在我必须 return 向用户说明失败原因之前没关系。
如何处理?
我能看到几个选项(但我都不喜欢):
实现一种结果对象:
public接口注册服务{
RegistrationResult 注册(字符串电子邮件,字符串名称);
}
public 界面注册服务{
布尔成功();
错误[]错误();
用户新用户();
}
很好,甚至可以用于 REST api。
但是是不是太麻烦了(特别是考虑到空白名称可能应该在工厂检查)?
抛出异常
public接口注册服务{
void Register(String email, String name) 抛出 RegistrationError;
}
看起来更准确一些。但例外是昂贵的。像这样使用它们看起来不是个好主意。
- 使用数据库约束。但是看起来比(2)还要乱。
让我们从第 3 点开始:数据库约束完成它们的工作。是的,exception/error 消息很乱,我同意这一点。但问问自己:什么更混乱:向具有相同电子邮件地址的 1 个用户或 2 个用户帐户显示的可怕错误消息可能会破坏您的系统?数据库约束应该是您最后的安全网。您的服务需要检查使用此电子邮件的用户帐户是否已存在。但是,如果在另一个线程中有人在您检查和创建新用户帐户之间的微秒内使用此电子邮件创建了一个用户帐户,会发生什么?您会对 DB 约束感到高兴。
是的,您可以找到更好的解决方案,但这需要您有一个单例服务来序列化所有帐户创建并确保没有两个线程可以同时创建用户帐户。
要点2:例外是针对特殊情况的。有人想要使用已使用的电子邮件创建用户帐户的情况是一种例外情况。在有人想做一些肮脏的事情的情况下,不要担心昂贵的操作。
第 1 点:我不喜欢这个。但那只是我的个人意见。在某些情况下,这种结果对象是有意义的,但我尽量将其保持在最低限度。
不同的层可以有不同的方式来考虑和发出问题信号。仅仅因为基础架构引发异常并不意味着其他所有层都应该这样做。
你可以:
- 基础架构:抛出重复键异常,因为预计客户端不会威胁数据库的完整性
- 应用程序:捕获异常和 returns 一个简单的
RegistrationResult.Failure
值,因为它是预期的失败案例
- 演示:returns HTTP 409 冲突
假设我需要在我的系统中注册用户。
商业规则是:
- 电子邮件应该是唯一的(一种身份);
- 名字不能为空。
看来我需要服务。
可能是这样的:
public interface RegistrationService {
bool Register(String email, String name);
}
在我必须 return 向用户说明失败原因之前没关系。 如何处理?
我能看到几个选项(但我都不喜欢):
实现一种结果对象:
public接口注册服务{ RegistrationResult 注册(字符串电子邮件,字符串名称); } public 界面注册服务{ 布尔成功(); 错误[]错误(); 用户新用户(); }
很好,甚至可以用于 REST api。 但是是不是太麻烦了(特别是考虑到空白名称可能应该在工厂检查)?
抛出异常
public接口注册服务{ void Register(String email, String name) 抛出 RegistrationError; }
看起来更准确一些。但例外是昂贵的。像这样使用它们看起来不是个好主意。
- 使用数据库约束。但是看起来比(2)还要乱。
让我们从第 3 点开始:数据库约束完成它们的工作。是的,exception/error 消息很乱,我同意这一点。但问问自己:什么更混乱:向具有相同电子邮件地址的 1 个用户或 2 个用户帐户显示的可怕错误消息可能会破坏您的系统?数据库约束应该是您最后的安全网。您的服务需要检查使用此电子邮件的用户帐户是否已存在。但是,如果在另一个线程中有人在您检查和创建新用户帐户之间的微秒内使用此电子邮件创建了一个用户帐户,会发生什么?您会对 DB 约束感到高兴。 是的,您可以找到更好的解决方案,但这需要您有一个单例服务来序列化所有帐户创建并确保没有两个线程可以同时创建用户帐户。
要点2:例外是针对特殊情况的。有人想要使用已使用的电子邮件创建用户帐户的情况是一种例外情况。在有人想做一些肮脏的事情的情况下,不要担心昂贵的操作。
第 1 点:我不喜欢这个。但那只是我的个人意见。在某些情况下,这种结果对象是有意义的,但我尽量将其保持在最低限度。
不同的层可以有不同的方式来考虑和发出问题信号。仅仅因为基础架构引发异常并不意味着其他所有层都应该这样做。
你可以:
- 基础架构:抛出重复键异常,因为预计客户端不会威胁数据库的完整性
- 应用程序:捕获异常和 returns 一个简单的
RegistrationResult.Failure
值,因为它是预期的失败案例 - 演示:returns HTTP 409 冲突