在Java中定义错误代码/string的最佳方法?

我正在用Java编写一个Web服务,我想弄清楚定义错误代码及其相关错误string的最佳方法 。 我需要将数字错误代码和错误string分组在一起。 错误代码和错误string都将被发送到访问Web服务的客户端。 例如,发生SQLException时,我可能需要执行以下操作:

// Example: errorCode = 1, // errorString = "There was a problem accessing the database." throw new SomeWebServiceException(errorCode, errorString); 

客户端程序可能会显示消息:

“出现错误#1:访问数据库时出现问题。”

我的第一个想法是使用错误代码的Enum并重写toString方法来返回错误string。 这是我想出来的:

 public enum Errors { DATABASE { @Override public String toString() { return "A database error has occured."; } }, DUPLICATE_USER { @Override public String toString() { return "This user already exists."; } }, // more errors follow } 

我的问题是:有没有更好的方法来做到这一点? 我更喜欢代码解决scheme,而不是从外部文件读取。 我在这个项目中使用了Javadoc,并且能够在线logging错误代码,并让它们在文档中自动更新会有帮助。

那么肯定有一个更好的实施的枚举解决scheme(这通常是相当不错的):

 public enum Error { DATABASE(0, "A database error has occured."), DUPLICATE_USER(1, "This user already exists."); private final int code; private final String description; private Error(int code, String description) { this.code = code; this.description = description; } public String getDescription() { return description; } public int getCode() { return code; } @Override public String toString() { return code + ": " + description; } } 

你可能想重写toString()来返回描述,而不是确定。 无论如何,重点是你不需要为每个错误代码分别覆盖。 另外请注意,我明确指定了代码,而不是使用序号值 – 这样以后可以更轻松地更改顺序并添加/删除错误。

不要忘记,这根本不是国际化的,但除非你的networking服务客户端向你发送一个区域描述,否则无论如何你都不能轻易地将其国际化。 至less他们会在客户端使用i18n的错误代码。

就我而言,我更喜欢外部化属性文件中的错误消息。 如果您的应用程序国际化(每种语言一个属性文件),这将非常有用。 修改错误信息也比较容易,不需要重新编译Java源代码。

在我的项目中,通常我有一个接口,其中包含错误代码(string或整数,它并不在乎),其中包含此错误的属性文件中的键:

 public interface ErrorCodes { String DATABASE_ERROR = "DATABASE_ERROR"; String DUPLICATE_USER = "DUPLICATE_USER"; ... } 

在属性文件中:

 DATABASE_ERROR=An error occurred in the database. DUPLICATE_USER=The user already exists. ... 

你的解决scheme的另一个问题是可维护性:你只有2个错误,并且已经有12行代码。 所以想象一下你的枚举文件,当你将有数百个错误pipe理!

重载toString()看起来有点恶心 – 这似乎是toString()正常使用的一小段。

关于什么:

 public enum Errors { DATABASE(1, "A database error has occured."), DUPLICATE_USER(5007, "This user already exists."); //... add more cases here ... private final int id; private final String message; Errors(int id, String message) { this.id = id; this.message = message; } public int getId() { return id; } public String getMessage() { return message; } } 

对我来说似乎更清洁…而且不那么冗长。

在我上一份工作中,我更加深入地研究了枚举版本:

 public enum Messages { @Error @Text("You can''t put a {0} in a {1}") XYZ00001_CONTAINMENT_NOT_ALLOWED, ... } 

@Error,@Info,@Warning保留在类文件中并在运行时可用。 (我们还有其他一些注释来帮助描述消息传递)

@Text是一个编译时注释。

我为此写了一个注释处理程序,它执行以下操作:

  • 确认没有重复的消息号码(第一个下划线之前的部分)
  • 语法 – 检查消息文本
  • 生成一个messages.properties文件,其中包含由枚举值所键入的文本。

我写了一些帮助logging错误的实用程序,将它们包装为例外(如果需要)等等。

我试图让他们让我开源 – 斯科特

我build议你看看java.util.ResourceBundle。 你应该关心I18N,但即使你不这样做也是值得的。 将消息外化是一个非常好的主意。 我发现能够给业务人员提供一个电子表格是很有用的,这使得他们可以用他们想要看到的确切语言。 我们编写了一个Ant任务,在编译时生成.properties文件。 这使得I18N变得微不足道。

如果你也使用Spring,那就更好了。 他们的MessageSource类对于这些事情是有用的。

只是为了防止这种特殊的死马 – 当向最终用户显示错误时,我们已经很好地使用了数字错误代码 ,因为他们经常忘记或误读实际的错误信息,但有时可能会保留并报告一个数字值你知道究竟发生了什么。

我(和我们公司的其他人)倾向于提出exception而不是返回错误代码。 错误代码必须在任何地方检查,传递,并且当代码量变大时,往往会使代码不可读。

错误类将定义消息。

PS:其实还关心国际化!
PPS:你也可以重新定义raise方法,并添加日志logging,如果需要过滤等(至less在环境中,exception类和朋友是可扩展/可更改的)

有点晚了,但我只是为自己寻找一个漂亮的解决scheme。 如果您有不同types的消息错误,则可以添加简单的自定义消息工厂,以便稍后指定更多详细信息和格式。

 public enum Error { DATABASE(0, "A database error has occured. "), DUPLICATE_USER(1, "User already exists. "); .... private String description = ""; public Error changeDescription(String description) { this.description = description; return this; } .... } Error genericError = Error.DATABASE; Error specific = Error.DUPLICATE_USER.changeDescription("(Call Admin)"); 

编辑:好吧,在这里使用枚举是有点危险,因为你永久地改变特定的枚举。 我想更好的是要改变类和使用静态字段,但比你不能再使用'=='。 所以我想这是一个很好的例子,不要做什么,(或只在初始化过程中):)

有很多方法可以解决这个问题。 我的首选方法是有接口:

 public interface ICode { /*your preferred code type here, can be int or string or whatever*/ id(); } public interface IMessage { ICode code(); } 

现在你可以定义任何数量的提供消息的枚举:

 public enum DatabaseMessage implements IMessage { CONNECTION_FAILURE(DatabaseCode.CONNECTION_FAILURE, ...); } 

现在你有几个选项把它们变成string。 您可以将string编译到代码中(使用注释或枚举构造函数参数),也可以从configuration/属性文件或数据库表或混合文件读取它们。 后者是我的首选方法,因为您将始终需要一些可以在很早的时候转换为文本的消息(即在连接到数据库或读取configuration时)。

我正在使用unit testing和reflection框架来查找实现我的接口的所有types,以确保每个代码在某处使用,并且configuration文件包含所有预期的消息等。

使用可以parsingJava的框架,比如https://github.com/javaparser/javaparser或Eclipse的框架,甚至可以检查枚举的使用位置,find未使用的枚举。;

枚举错误代码/消息定义仍然是一个不错的解决scheme,虽然它有一个国际关注。 实际上,我们可能有两种情况:代码/消息显示给最终用户或系统集成商。 对于后面的情况,I18N没有必要。 我认为networking服务很可能是后面的情况。

使用interface作为消息常量通常是一个坏主意。 它将永久泄漏到客户端程序中,作为导出的API的一部分。 谁知道,后来的客户端程序员可能会将这些错误信息(公共)作为其程序的一部分进行parsing。

你将永远被locking,以支持这一点,因为string格式的改变将会破坏客户端程序。

请按照下面的例子:

 public enum ErrorCodes { NO_File("No file found. "), private ErrorCodes(String value) { this.errordesc = value; } private String errordesc = ""; public String errordesc() { return errordesc; } public void setValue(String errordesc) { this.errordesc = errordesc; } 

};

在你的代码中调用它像:

 fileResponse.setErrorCode(ErrorCodes.NO_FILE.errordesc());