我正在编写一个连接到服务的API,该服务要么返回一条简单的“成功”消息,要么返回100多种不同类型的失败消息。
最初我想写一个向这个服务发送请求的方法,如果它成功了,那么该方法什么都不返回,但如果它因任何原因失败,它会抛出异常。
我不太介意这个设计,但另一方面,就在今天,我正在阅读Joshua Bloch的“How to Design a Good API and Why it Matters”,他在那里说“抛出异常以表明特殊情况......不要强迫客户端使用控制流的异常。“ (和“相反,不要默默地失败。”)
另一方面,我注意到我正在使用的HttpWebRequest似乎在请求失败时抛出异常,而不是返回包含“500 Internal Server Error”消息的Response。
在这种情况下,报告错误的最佳模式是什么?如果我在每次失败的请求中抛出异常,那么我将来会在某个时刻感受到巨大的痛苦吗?
编辑:非常感谢您对目前为止的回复。一些细化:
ChargeCreditCard(CreditCardInfo i)
- 显然当ChargeCreditCard()方法失败时,它是一个巨大的交易;我只是不确定是否应该停止印刷机或将责任交给客户。编辑第二个: 基本上我并不完全相信使用这两种方法中的哪一种:
try {
ChargeCreditCard(cardNumber, expDate, hugeAmountOMoney);
} catch(ChargeFailException e) {
// client handles error depending on type of failure as specified by specific type of exception
}
或
var status = TryChargeCreditCard(cardNumber, expDate, hugeAmountOMoney);
if(!status.wasSuccessful) {
// client handles error depending on type of failure as specified in status
}
e.g。当用户试图通过信用卡收费时,该卡是否被拒绝了特殊情况?我一开始就问这个问题,我是否会在兔子洞里走得太远?
答案 0 :(得分:6)
这是一个需要考虑的简短列表。虽然不全面,但我相信这些东西可以帮助您编写更好的代码。底线:不一定将异常处理视为邪恶。相反,在编写它们时,请问自己:我真正了解我正在解决的问题有多好?通常情况下,这将有助于您成为更好的开发人员。
ServiceConnectionException
与令人困惑的ServiceDisconnectedConnectionStatusException
Car GetCarFromBigString(string desc)
的方法,它接受一个字符串并返回一个Car
对象。如果该方法的大多数用例是从Car
生成string
对象,则在{{1}无法确定Car
时不要抛出异常}}。相反,请编写类似string
。为了代码可读性,我们可以看一下你的上下文。
bool TryGetCarFromBigString(string desc, out Car)
您可以在上面看到,如果上下文只是一个二进制测试,那么捕获样本3中的bool IsServiceAlive()
{
bool connected = false; //bool is always initialized to false, but for readability in this context
try
{
//Some check
Service.Connect();
connected = true;
}
catch (CouldNotConnectToSomeServiceException)
{
//Do what you need to do
}
return connected;
}
//or
void IsServiceAlive()
{
try
{
//Some check
Service.Connect();
}
catch (CouldNotConnectToSomeServiceException)
{
//Do what you need to do
throw;
}
}
static void Main(string[] args)
{
//sample 1
if (IsServiceAlive())
{
//do something
}
//sample 2
try
{
if (IsServiceAlive())
{
//do something
}
}
catch (CouldNotConnectToSomeServiceException)
{
//handle here
}
//sample 3
try
{
IsServiceAlive();
//work
}
catch (CouldNotConnectToSomeServiceException)
{
//handle here
}
}
并不一定会产生更好的可读性。但是,两者都有效。但这真的有必要吗?如果您无法连接,您的程序是否已被清除?真的有多重要?这些都是您需要考虑的因素。由于我们无法访问您的所有代码,因此很难说清楚。
让我们来看看最有可能导致问题的其他一些选项。
CouldNotConnectToSomeServiceException
和
//how will the code look when you have to do 50 string comparisons? Not pretty or scalable.
public class ServiceConnectionStatus
{
public string Description { get; set; }
}
答案 1 :(得分:3)
我认为您需要考虑设计中的一些事项:
1)如何访问API?如果您通过Web服务公开它,那么抛出异常可能不是一个好主意。如果API位于您提供供人们在其应用程序中引用的DLL中,则异常可能没问题。
2)为了使失败响应对API使用者有用,需要使用返回值传输多少额外数据?如果您需要在失败消息(即用户ID和登录)中提供可用信息而不是嵌入了该信息的字符串,那么您可以使用自定义异常或包含错误代码和其他可用信息的“ErrorEncountered”类。如果您只需要传回代码,则表示成功(0)或失败(任何非零值)的ENum可能是合适的。
3)在原始响应中忘记了这一点:.Net框架中的异常代价很高。如果您的API将在一段时间内被调用一次,则无需考虑因素。但是,如果为在高流量站点中提供的每个网页调用API,例如,您绝对不希望抛出异常以表示请求失败。
所以简短的回答是,它确实取决于具体情况。
答案 2 :(得分:2)
我非常喜欢“抛出异常来表示特殊条件”的想法。他们必须有这个名字是有原因的。
在常规应用程序中,您可以在File.Exists()
之前使用File.Open()
来防止抛出异常。由于异常难以处理,因此预期的错误。
在客户端 - 服务器环境中,您可能希望防止必须发送两个请求并创建FileOpenResponse
类来发送状态和数据(例如文件句柄,在这种情况下)。