Wysyłanie i odbieranie błędów
Błędy protokołu SOAP przekazują informacje o stanie błędu z usługi do klienta i w przypadku dwukierunkowym od klienta do usługi w sposób współdziałania. Zazwyczaj usługa definiuje niestandardową zawartość błędów i określa, które operacje mogą je zwracać. (Aby uzyskać więcej informacji, zobacz Definiowanie i określanie błędów). W tym temacie opisano, jak klient usługi lub dwukierunkowy może wysyłać te błędy po wystąpieniu odpowiedniego warunku błędu i jak klient lub aplikacja usługi obsługuje te błędy. Aby zapoznać się z omówieniem obsługi błędów w aplikacjach programu Windows Communication Foundation (WCF), zobacz Określanie i obsługa błędów w kontraktach i usługach.
Wysyłanie błędów protokołu SOAP
Zadeklarowane błędy protokołu SOAP to te, w których operacja ma System.ServiceModel.FaultContractAttribute określony niestandardowy typ błędu PROTOKOŁU SOAP. Błędy protokołu SOAP niezdecydowane to te, które nie zostały określone w umowie dotyczącej operacji.
Wysyłanie zadeklarowanych błędów
Aby wysłać zadeklarowany błąd PROTOKOŁU SOAP, wykryj warunek błędu, dla którego jest odpowiedni błąd protokołu SOAP i zgłosić nowy System.ServiceModel.FaultException<TDetail> , gdzie parametr typu jest nowym obiektem typu określonego FaultContractAttribute w tej operacji. Poniższy przykład kodu pokazuje użycie metody FaultContractAttribute , aby określić, że SampleMethod
operacja może zwrócić błąd PROTOKOŁU SOAP ze szczegółowym typem GreetingFault
.
[OperationContract]
[FaultContractAttribute(
typeof(GreetingFault),
Action="http://www.contoso.com/GreetingFault",
ProtectionLevel=ProtectionLevel.EncryptAndSign
)]
string SampleMethod(string msg);
<OperationContract, FaultContractAttribute(GetType(GreetingFault), Action:="http://www.contoso.com/GreetingFault", ProtectionLevel:=ProtectionLevel.EncryptAndSign)> _
Function SampleMethod(ByVal msg As String) As String
Aby przekazać klientowi GreetingFault
informacje o błędzie, przechwyć odpowiedni warunek błędu i zgłosić nowy System.ServiceModel.FaultException<TDetail> typ GreetingFault
z nowym GreetingFault
obiektem jako argumentem, jak w poniższym przykładzie kodu. Jeśli klient jest aplikacją kliencką WCF, występuje to jako wyjątek zarządzany, w którym typ jest System.ServiceModel.FaultException<TDetail> typu GreetingFault
.
throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred. You said: " + msg));
Throw New FaultException(Of GreetingFault)(New GreetingFault("A Greeting error occurred. You said: " & msg))
End If
Wysyłanie niezdecydowanych błędów
Wysyłanie niezdecydowanych błędów może być bardzo przydatne do szybkiego diagnozowania i debugowania problemów w aplikacjach WCF, ale jego użyteczność jako narzędzie do debugowania jest ograniczona. Ogólnie rzecz biorąc, podczas debugowania zaleca się użycie ServiceDebugBehavior.IncludeExceptionDetailInFaults właściwości . Po ustawieniu tej wartości na true klienci doświadczają takich błędów jak FaultException<TDetail> wyjątki typu ExceptionDetail.
Ważne
Ponieważ wyjątki zarządzane mogą uwidaczniać informacje o aplikacji wewnętrznej, ustawienie ServiceBehaviorAttribute.IncludeExceptionDetailInFaults lub ServiceDebugBehavior.IncludeExceptionDetailInFaultstrue
zezwalać klientom WCF na uzyskiwanie informacji o wyjątkach operacji usługi wewnętrznej, w tym danych osobowych lub innych poufnych informacji.
W związku z tym ustawienie lub ServiceDebugBehavior.IncludeExceptionDetailInFaults ustawienie ServiceBehaviorAttribute.IncludeExceptionDetailInFaults na true
jest zalecane tylko jako sposób tymczasowego debugowania aplikacji usługi. Ponadto język WSDL dla metody zwracającej nieobsługiwane wyjątki zarządzane w ten sposób nie zawiera kontraktu FaultException<TDetail>ExceptionDetailtypu . Klienci muszą oczekiwać możliwości wystąpienia nieznanego błędu protokołu SOAP (zwróconego do klientów programu WCF jako System.ServiceModel.FaultException obiektów), aby prawidłowo uzyskać informacje o debugowaniu.
Aby wysłać nierejestrowany błąd protokołu SOAP, wyrzuć System.ServiceModel.FaultException obiekt (a nie typ FaultException<TDetail>ogólny ) i przekaż ciąg do konstruktora. Jest to widoczne dla aplikacji klienckich WCF jako zgłoszony System.ServiceModel.FaultException wyjątek, w którym ciąg jest dostępny przez wywołanie FaultException<TDetail>.ToString metody .
Uwaga
Jeśli zadeklarujesz błąd protokołu SOAP typu ciąg, a następnie zgłosisz go w usłudze jako parametr, w FaultException<TDetail> którym parametr typu jest wartością System.String ciągu jest przypisana do FaultException<TDetail>.Detail właściwości i nie jest dostępny z FaultException<TDetail>.ToString.
Obsługa błędów
W przypadku klientów programu WCF błędy protokołu SOAP występujące podczas komunikacji, które są interesujące dla aplikacji klienckich, są zgłaszane jako wyjątki zarządzane. Chociaż istnieje wiele wyjątków, które mogą wystąpić podczas wykonywania dowolnego programu, aplikacje korzystające z modelu programowania klienta WCF mogą oczekiwać obsługi wyjątków następujących dwóch typów w wyniku komunikacji.
TimeoutException obiekty są zgłaszane, gdy operacja przekracza określony limit czasu.
CommunicationException w przypadku wystąpienia błędu komunikacji możliwego do odzyskania w usłudze lub kliencie są zgłaszane obiekty.
Klasa CommunicationException ma dwa ważne typy pochodne i FaultException typ ogólny FaultException<TDetail> .
FaultException wyjątki są zgłaszane, gdy odbiornik otrzymuje błąd, który nie jest oczekiwany lub określony w kontrakcie operacji; zwykle dzieje się tak, gdy aplikacja jest debugowana, a usługa ma właściwość ustawioną ServiceDebugBehavior.IncludeExceptionDetailInFaults na true
.
FaultException<TDetail> wyjątki są zgłaszane na kliencie, gdy błąd określony w kontrakcie operacji jest odbierany w odpowiedzi na operację dwukierunkową (czyli metodę z OperationContractAttribute atrybutem z IsOneWay ustawionym na false
).
Uwaga
Gdy usługa WCF ma ServiceBehaviorAttribute.IncludeExceptionDetailInFaults właściwość or ServiceDebugBehavior.IncludeExceptionDetailInFaults ustawioną na true
klienta, występuje to jako niezadeklarowane FaultException<TDetail> typu ExceptionDetail. Klienci mogą przechwytywać tę konkretną usterkę lub obsługiwać błąd w bloku catch dla elementu FaultException.
Zazwyczaj tylko FaultException<TDetail>wyjątki , TimeoutExceptioni CommunicationException są interesujące dla klientów i usług.
Uwaga
Inne wyjątki, oczywiście, występują. Nieoczekiwane wyjątki obejmują katastrofalne błędy, takie jak System.OutOfMemoryException; zazwyczaj aplikacje nie powinny przechwytywać takich metod.
Przechwyć wyjątki błędów w prawidłowej kolejności
Ponieważ FaultException<TDetail> pochodzi z elementu i FaultException pochodzi z FaultExceptionCommunicationExceptionklasy , ważne jest, aby przechwycić te wyjątki w odpowiedniej kolejności. Jeśli na przykład masz blok try/catch, w którym najpierw przechwytujesz CommunicationException, wszystkie określone i nieokreślone błędy protokołu SOAP są tam obsługiwane; wszelkie kolejne bloki przechwytywania do obsługi wyjątku niestandardowego FaultException<TDetail> nigdy nie są wywoływane.
Pamiętaj, że jedna operacja może zwrócić dowolną liczbę określonych błędów. Każda usterka jest unikatowym typem i musi być obsługiwana oddzielnie.
Obsługa wyjątków podczas zamykania kanału
Większość powyższej dyskusji ma związek z błędami wysyłanymi w trakcie przetwarzania komunikatów aplikacji, czyli komunikatów jawnie wysyłanych przez klienta, gdy aplikacja kliencka wywołuje operacje na obiekcie klienta WCF.
Nawet w przypadku obiektów lokalnych dysponujących obiekt może zgłaszać lub maskować wyjątki występujące podczas procesu recyklingu. Podczas korzystania z obiektów klienta WCF może wystąpić coś podobnego. Podczas wywoływania operacji wysyłasz komunikaty za pośrednictwem ustanowionego połączenia. Zamknięcie kanału może zgłaszać wyjątki, jeśli połączenie nie może być poprawnie zamknięte lub zostało już zamknięte, nawet jeśli wszystkie operacje zostały zwrócone prawidłowo.
Zazwyczaj kanały obiektów klienta są zamykane na jeden z następujących sposobów:
Gdy obiekt klienta programu WCF jest poddawany recyklingu.
Gdy aplikacja kliencka wywołuje metodę ClientBase<TChannel>.Close.
Gdy aplikacja kliencka wywołuje metodę ICommunicationObject.Close.
Gdy aplikacja kliencka wywołuje operację, która jest operacją zakończenia sesji.
We wszystkich przypadkach zamknięcie kanału nakazuje kanałowi rozpoczęcie zamykania wszystkich kanałów bazowych, które mogą wysyłać komunikaty do obsługi złożonych funkcji na poziomie aplikacji. Na przykład gdy kontrakt wymaga sesji powiązania próbuje ustanowić sesję przez wymianę komunikatów z kanałem usługi do momentu ustanowienia sesji. Po zamknięciu kanału źródłowy kanał sesji powiadamia usługę o zakończeniu sesji. W takim przypadku, jeśli kanał został już przerwany, zamknięty lub jest w inny sposób bezużyteczny (na przykład gdy kabel sieciowy jest odłączony), kanał klienta nie może poinformować kanału usługi, że sesja została zakończona, a wyjątek może spowodować.
Przerwij kanał w razie potrzeby
Ponieważ zamknięcie kanału może również zgłaszać wyjątki, zaleca się, aby oprócz wychwytywania wyjątków błędów w prawidłowej kolejności, ważne jest przerwanie kanału użytego podczas wykonywania wywołania w bloku catch.
Jeśli błąd przekazuje informacje o błędach specyficzne dla operacji i istnieje możliwość, że inne osoby mogą z niego korzystać, nie ma potrzeby przerwania kanału (chociaż te przypadki są rzadkie). We wszystkich innych przypadkach zaleca się przerwanie kanału. Aby zapoznać się z przykładem przedstawiającym wszystkie te punkty, zobacz Oczekiwane wyjątki.
Poniższy przykład kodu pokazuje, jak obsługiwać wyjątki błędów protokołu SOAP w podstawowej aplikacji klienckiej, w tym zadeklarowany błąd i niezdecydowany błąd.
Uwaga
Ten przykładowy kod nie używa using
konstrukcji. Ponieważ kanały zamykające mogą zgłaszać wyjątki, zaleca się, aby aplikacje najpierw tworzyły klienta WCF, a następnie otwierały, używały i zamykały klienta programu WCF w tym samym bloku try. Aby uzyskać szczegółowe informacje, zobacz Omówienie klienta programu WCF i Używanie zamykania i przerwania w celu wydania zasobów klienta programu WCF.
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;
public class Client
{
public static void Main()
{
// Picks up configuration from the config file.
SampleServiceClient wcfClient = new SampleServiceClient();
try
{
// Making calls.
Console.WriteLine("Enter the greeting to send: ");
string greeting = Console.ReadLine();
Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));
Console.WriteLine("Press ENTER to exit:");
Console.ReadLine();
// Done with service.
wcfClient.Close();
Console.WriteLine("Done!");
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (FaultException<GreetingFault> greetingFault)
{
Console.WriteLine(greetingFault.Detail.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (FaultException unknownFault)
{
Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
Console.ReadLine();
wcfClient.Abort();
}
}
}
Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation
Public Class Client
Public Shared Sub Main()
' Picks up configuration from the config file.
Dim wcfClient As New SampleServiceClient()
Try
' Making calls.
Console.WriteLine("Enter the greeting to send: ")
Dim greeting As String = Console.ReadLine()
Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))
Console.WriteLine("Press ENTER to exit:")
Console.ReadLine()
' Done with service.
wcfClient.Close()
Console.WriteLine("Done!")
Catch timeProblem As TimeoutException
Console.WriteLine("The service operation timed out. " & timeProblem.Message)
Console.ReadLine()
wcfClient.Abort()
Catch greetingFault As FaultException(Of GreetingFault)
Console.WriteLine(greetingFault.Detail.Message)
Console.ReadLine()
wcfClient.Abort()
Catch unknownFault As FaultException
Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
Console.ReadLine()
wcfClient.Abort()
Catch commProblem As CommunicationException
Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
Console.ReadLine()
wcfClient.Abort()
End Try
End Sub
End Class