2011-11-14 35 views
8

Tôi đang viết một API kết nối với một dịch vụ hoặc trả về một thông điệp "Thành công" đơn giản hoặc một trong hơn 100 mùi vị khác nhau của sự thất bại.Cách thực hành tốt nhất để chỉ ra rằng yêu cầu máy chủ đã thất bại?

Ban đầu tôi nghĩ viết phương thức gửi yêu cầu đến dịch vụ này sao cho nếu nó thành công thì phương thức trả về không có gì, nhưng nếu nó không thành công vì bất kỳ lý do nào, nó ném một ngoại lệ.

Tôi không quan tâm đến thiết kế này lắm, nhưng mặt khác hôm nay tôi đã đọc "How to Design a Good API and Why it Matters" của Joshua Bloch, nơi ông nói "Vứt bỏ ngoại lệ để chỉ ra điều kiện đặc biệt ... Đừng ép khách hàng sử dụng ngoại lệ cho luồng điều khiển. " (và "Ngược lại, đừng thất bại.")

Mặt khác, tôi nhận thấy rằng HttpWebRequest tôi đang sử dụng dường như ném một ngoại lệ khi yêu cầu không thành công, thay vì trả về một phản hồi chứa " 500 Lỗi máy chủ nội bộ ".

Mẫu tốt nhất để báo cáo lỗi trong trường hợp này là gì? Nếu tôi ném một ngoại lệ đối với mọi yêu cầu không thành công, tôi có phải chịu đau khổ lớn tại một thời điểm nào đó trong tương lai không?

Chỉnh sửa: Cảm ơn bạn rất vui vì đã trả lời cho đến thời điểm này. Một số công cụ xây dựng:

  • đó là một DLL sẽ được cung cấp cho khách hàng để tham khảo trong đơn đăng ký của họ.
  • một ví dụ tương tự về cách sử dụng sẽ là ChargeCreditCard(CreditCardInfo i) - rõ ràng là khi phương thức ChargeCreditCard() không thành công, đó là một thỏa thuận lớn; Tôi chỉ không chắc chắn 100% liệu tôi có nên dừng máy ép hoặc chuyển trách nhiệm đó cho khách hàng hay không.

Sửa lần thứ hai: Về cơ bản Tôi không hoàn toàn thuyết phục nào trong hai phương thức này để sử dụng:

try { 
ChargeCreditCard(cardNumber, expDate, hugeAmountOMoney); 
} catch(ChargeFailException e) { 
    // client handles error depending on type of failure as specified by specific type of exception 
} 

hoặc

var status = TryChargeCreditCard(cardNumber, expDate, hugeAmountOMoney); 

if(!status.wasSuccessful) { 
    // client handles error depending on type of failure as specified in status 
} 

ví dụ khi người dùng cố gắng tính phí thẻ tín dụng, thẻ có bị từ chối thực sự là một trường hợp đặc biệt đặc biệt không? Tôi đang đi quá xa trong lỗ thỏ bằng cách hỏi câu hỏi này ngay từ đầu?

+1

"Exceptions, trường hợp ngoại lệ, ngoại lệ!" (* Ballmer dance *) –

+1

Đây có phải là REST, hoặc SOAP, hoặc cả hai? –

+0

'WebException' được ném bởi' WebRequest 'GetResponse()' chứa 'WebResponse' nếu nó là _valid_ theo định dạng giao thức. Vì vậy, đối với bạn, nó hoặc là giữ thiết kế của 'BCL' lớp học và xây dựng một cái gì đó tương tự hoặc ... không. Mục tiêu của ứng dụng của bạn là gì? Có cần thiết rằng yêu cầu máy chủ thành công không? Sau đó ném. Hay nó chỉ là tốt đẹp để có dữ liệu bổ sung? – ordag

Trả lời

6

Dưới đây là danh sách ngắn các điều cần xem xét. Trong khi không toàn diện, tôi tin rằng những điều này có thể giúp bạn viết mã tốt hơn. Điểm mấu chốt: Không nhất thiết phải coi việc xử lý ngoại lệ là điều xấu. Thay vào đó, khi viết chúng, hãy tự hỏi: Làm thế nào để tôi thực sự hiểu được vấn đề tôi đang giải quyết? Thường xuyên hơn không, điều này sẽ giúp bạn trở thành một nhà phát triển tốt hơn.

  1. Các nhà phát triển khác có thể đọc được điều này không? Có thể hợp lý là hiểu nhà phát triển trung bình là không? Ví dụ: ServiceConnectionException và khó hiểu ServiceDisconnectedConnectionStatusException
  2. Trong trường hợp ném ngoại lệ, cách ngoại lệ là trường hợp nào? Người gọi phải làm gì để thực hiện phương pháp?
  3. Đây có phải là ngoại lệ gây tử vong không? Bất cứ điều gì thực sự có thể được thực hiện với ngoại lệ này nếu nó bị bắt? Chủ đề hủy bỏ, ra khỏi bộ nhớ .. bạn không thể làm bất cứ điều gì hữu ích. Đừng bắt nó.
  4. Ngoại lệ có gây nhầm lẫn không? Giả sử bạn có một phương thức được gọi là Car GetCarFromBigString(string desc) lấy một chuỗi và trả về đối tượng Car. Nếu trường hợp sử dụng đa số cho phương pháp đó là tạo đối tượng Car từ đó string, không ném ngoại lệ khi không thể xác định được Car từ số string. Thay vào đó, hãy viết một phương thức như bool TryGetCarFromBigString(string desc, out Car).
  5. Điều này có thể dễ dàng ngăn chặn không? Tôi có thể kiểm tra một cái gì đó, chúng ta hãy nói kích thước của một mảng hoặc một biến được null?

Vì lý do khả năng đọc mã, chúng ta hãy xem xét ngữ cảnh của bạn.

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 
    } 
} 

Bạn có thể thấy ở trên, mà bắt CouldNotConnectToSomeServiceException trong mẫu 3 không nhất thiết mang lại bất kỳ khả năng đọc tốt hơn nếu bối cảnh chỉ đơn giản là một thử nghiệm nhị phân. Tuy nhiên, cả hai công việc. Nhưng nó thực sự cần thiết? Chương trình của bạn có bị hosed nếu bạn không thể kết nối không? Làm thế nào quan trọng là nó thực sự? Đây là tất cả các yếu tố bạn sẽ cần phải đưa vào tài khoản. Thật khó để nói vì chúng tôi không có quyền truy cập vào tất cả các mã của bạn.

Hãy xem xét một số tùy chọn khác có nhiều khả năng dẫn đến sự cố.

//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; } 
} 

//how will your code look after adding 50 more of these? 
public enum ServiceConnectionStatus 
{ 
    Success, 
    Failure, 
    LightningStormAtDataCenter, 
    UniverseExploded 
} 
3

Tôi nghĩ bạn cần cân nhắc một vài điều trong thiết kế của mình:

1) API sẽ được truy cập như thế nào? Nếu bạn đang phơi bày nó trên các dịch vụ web, thì việc ném ngoại lệ có lẽ không phải là một ý tưởng hay. Nếu API nằm trong một DLL mà bạn đang cung cấp để mọi người tham khảo trong các ứng dụng của họ, thì các trường hợp ngoại lệ có thể được chấp nhận.

2) Cần thêm bao nhiêu dữ liệu để đi du lịch với giá trị trả về để làm cho phản hồi thất bại hữu ích cho người tiêu dùng API? Nếu bạn cần cung cấp thông tin có thể sử dụng được trong thông báo lỗi của bạn (ví dụ id người dùng và đăng nhập) trái ngược với chuỗi có thông tin đó được nhúng, thì bạn có thể sử dụng ngoại lệ tùy chỉnh hoặc lớp "ErrorEncountered" có chứa mã lỗi và thông tin có thể sử dụng khác . Nếu bạn chỉ cần vượt qua một mã trở lại, sau đó một ENum chỉ ra một trong hai thành công (0) hoặc thất bại (bất kỳ giá trị khác không) có thể thích hợp.

3) Quên điều này trong phản hồi ban đầu: các trường hợp ngoại lệ rất tốn kém trong khung .Net. Nếu API của bạn sẽ được gọi một lần trong một thời gian, điều này không cần phải tính đến.Tuy nhiên, nếu API được gọi cho mỗi trang web được phân phối trong một trang web có lưu lượng truy cập cao, ví dụ, bạn chắc chắn không muốn ném ngoại lệ để chỉ ra một yêu cầu thất bại.

Vì vậy, câu trả lời ngắn gọn, là nó thực sự phụ thuộc vào hoàn cảnh chính xác.

2

Tôi thực sự thích ý tưởng "Ném ngoại lệ để chỉ ra điều kiện ngoại lệ". Họ phải có tên đó vì một lý do.

Trong ứng dụng thông thường, bạn sẽ sử dụng File.Exists() trước khi File.Open() để ngăn ngoại lệ bị ném. Lỗi dự kiến ​​là ngoại lệ khó xử lý. Tuy nhiên, trong môi trường máy chủ-máy khách, bạn có thể muốn ngăn gửi hai yêu cầu và tạo một lớp FileOpenResponse để gửi cả trạng thái và dữ liệu (chẳng hạn như một xử lý tệp, trong trường hợp này).

+2

Mặt trái của 'ngoại lệ là ngoại lệ' - (từ tài liệu MSDN và các nguyên tắc thiết kế khuôn khổ .NET): "Ngoại lệ là cơ chế tiêu chuẩn cho các lỗi báo cáo". Ngoài ra: http://blogs.msdn.com/b/kcwalina/archive/2008/07/17/exceptionerror.aspx –

+0

Mặc dù một ứng dụng có thể xử lý các loại ngoại lệ khác nhau một cách khác nhau, tôi thà thấy một API với 'TryAlloc() 'phương thức hơn một với' Alloc() 'ném một' OutOfMemoryException' (xem ví dụ trong liên kết). Nó sẽ là ngớ ngẩn đối với tôi để không thừa nhận kinh nghiệm của những người viết các hướng dẫn này mặc dù. Ít nhất nó đã cho tôi suy nghĩ lại :) –

Các vấn đề liên quan