|
|
Browse by Tags
All Tags » Faults » Messages (RSS)
-
When using a typed contract, incoming messages on the server are shredded on your behalf to be turned into method calls and parameters. Ordinarily, the particular method call selected for an application messages will have the same parameterized contract as the message. This allows the transformation between messages and parameters to be made with a high degree of fidelity. However, operations also permit fault responses in addition to the normal application response. The parameterized fault contract is going to look nothing like the standard application contract. Therefore, there's really no transformation that will take you from the fault message to the same parameterized contract in a way that makes any sense. The response that comes back from the server is unrepresentable using the standard data structure that the application is expecting to receive for an application response. This is why with a typed contract fault messages have to be expressed as an exceptional condition. Exceptions tend to transform the message with a much lower fidelity to the original content. With an untyped contract, incoming messages on the server are not shredded on your behalf but rather preserved in their entirety. You can think about this as performing the transformation between messages and parameters with perfect fidelity since the parameter is equal to the message. Similarly, any message response, whether it's a fault response or a normal application response, is also going to be representable with perfect fidelity. Both types of responses have the same format with an untyped contract so the application can handle them equally well. This is why with an untyped contract fault messages are preserved as messages. One of the major reasons for using untyped contracts is to have great fidelity with the wire. It wouldn't make sense to force the application to lose that fidelity for a certain class of messages. If you choose to in your application though, you can still run the same exception machinery. For details, read some of the past articles on creating and consuming faults . Next time: Streaming Web Content Read More...
|
-
The code yesterday was meant to motivate a side-discussion on how faults get generated and handled between the server and client proxy. If you tried running that sample, then you would have seen that despite the FaultException being thrown on the service, the service call completes normally. The return value of the service call is a fault message. If you've been writing your contracts with typed messages instead of the raw Message type, then this is the opposite behavior to what you're used to seeing. Using the same pattern for exception handling doesn't work between typed and untyped messages. This is particularly messy when you have a mix of typed and untyped operation contracts on the same service because it requires some duplicated logic for handling errors. However, I think that would be a pretty rare service design. There are four cases that I think are interesting to look at so that you can see the different fault behaviors that could occur. Untyped fault exception with an untyped message contract The basic case from yesterday is to receive a fault message. < s:Envelope xmlns:s ="http://www.w3.org/2003/05/soap-envelope" xmlns:a ="http://www.w3.org/2005/08/addressing" > < s:Header > < a:Action s:mustUnderstand ="1" > http://www.w3.org/2005/08/addressing/soap/fault </ a:Action > < a:RelatesTo > urn:uuid:dd129ffe-a8ff-4a70-ad6f-ad48085e94e8 </ a:RelatesTo > < a:To s:mustUnderstand ="1" > http://www.w3.org/2005/08/addressing/anonymous </ a:To > </ s:Header > < s:Body > < s:Fault > < s:Code > < s:Value > s:Sender </ s:Value > </ s:Code > < s:Reason > < s:Text xml:lang ="en-US" > boo! </ s:Text > </ s:Reason > </ s:Fault > </ s:Body > </ s:Envelope > Typed fault exception with an untyped message contract I'm just changing the FaultException to a FaultException here, although you can have any type you want for the fault detail. This changes the contents of the fault message but not the code path. Note that the action is different in addition to the detail section to match the parameterized type. < s:Envelope xmlns:s ="http://www.w3.org/2003/05/soap-envelope" xmlns:a ="http://www.w3.org/2005/08/addressing" > < s:Header > < a:Action s:mustUnderstand ="1" > http://tempuri.org/IService/VerbStringFault </ a:Action > < a:RelatesTo > urn:uuid:49ee87c7-691f-48c4-86ea-bb172c99294d </ a:RelatesTo > < Read More...
|
-
What does this code print? It seems like both choices are quite reasonable. I'll have some discussion about this tomorrow. [ServiceContract] interface IService { [OperationContract(Action= "foo" )] Message Verb(Message input); } class Service : IService { public Message Verb(Message input) { throw new FaultException( "boo!" ); } } class Program { static void Service() { ServiceHost host = new ServiceHost( typeof (Service), new Uri( "net.tcp://localhost/" )); host.AddServiceEndpoint( typeof (IService), new NetTcpBinding(), "" ); host.Open(); Console.ReadLine(); } static void Main( string [] args) { new Thread( new ThreadStart(Service)).Start(); Binding binding = new NetTcpBinding(); ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, "net.tcp://localhost/" ); IService proxy = factory.CreateChannel(); try { Message response = proxy.Verb(Message.CreateMessage(binding.MessageVersion, "foo" )); Console.WriteLine( "Received message" ); Console.WriteLine(response.ToString()); } catch (FaultException fault) { Console.WriteLine( "Received fault" ); Console.WriteLine(fault.ToString()); } } } Next time: A Trick with Faults (Discussion) Read More...
|
-
What should I set the action parameter to when creating a FaultException? There is indeed a pair of overloads for creating fault exceptions that take an action parameter, although most of the overloads lack this. public FaultException(TDetail detail, FaultReason reason, FaultCode code, string action); public FaultException(TDetail detail, string reason, FaultCode code, string action); What does the action parameter actually do? Well, this may or may not be obvious, but setting the action on the fault exception controls the action that is used when sending the fault message. This is the reason why you can't just make up an action here and expect it to work. The receiver is looking for a particular action to reconstitute the fault message to an exception with the appropriate type. If you break the action here, then your typed FaultException turns into an untyped FaultException. The expected action value for a particular typed FaultException comes from the fault contract. If you just set up the fault contract and don't worry at all about the action when creating fault exceptions, then everything should work. The fault exception will automatically pick up the correct action from the fault contract. The answer then is that you shouldn't set the action parameter at all in most cases. The default fault contract action is generated by combining a number of type strings. For instance, if my service contract is IService, my operation is called Action, and I'm using a typed FaultException<string> instance, then the default fault action is http://tempuri.org/IService/ActionStringFault . Similarly, if I'm instead using a typed FaultException<IList<string>>, then the default fault action is http://tempuri.org/IService/ActionIListOf_StringFault . You can get as crazy as you want and figure out what the expected pattern should be for any type. Want to send an IDictionary<IList<string>, IDictionary<DateTime, string>>? It will be http://tempuri.org/IService/ActionIDictionaryOf_IListOf_String_IDictionaryOf_DateTime_StringFault . Of course, you can explicitly put an action in the fault contract attribute to set this value to anything. Next time: Transport Encryption and Signing Read More...
|
-
Let's pick up where we left off last time with the question… How do I modify the HTTP status code that gets sent back with a fault? It's clear that we need to plug into the fault generation process somehow, but in past articles we've only seen fault handling for channels rather than services. Is there an equivalent for FaultConverter that we can use with our service? Well, partially. There's an IErrorHandler interface that very slightly overlaps the functionality of FaultConverter, but fortunately that's exactly the part we need. IErrorHandler allows us to replace the message faults that get created by the service. We can modify the status code by attaching a message property to the fault for the HTTP response. Here's what that looks like. I'll change the normal internal server error status code to one that indicates that the service requires payment to make the method call. class HttpErrorHandler : IErrorHandler { public bool HandleError(Exception error) { return false ; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { if (fault != null ) { HttpResponseMessageProperty properties = new HttpResponseMessageProperty(); properties.StatusCode = HttpStatusCode.PaymentRequired; fault.Properties.Add(HttpResponseMessageProperty.Name, properties); } } } Now, we need some way to attach this error handler to the service. Since this example is using IIS hosting, there are fewer points to hook in as we don't own the service host. I'll create a new server behavior attribute that attaches an IErrorHandler instance to the service. As IErrorHandler interfaces with the service dispatcher, I need to fish out the dispatchers from the service host. class ErrorBehaviorAttribute : Attribute, IServiceBehavior { Type errorHandlerType; public ErrorBehaviorAttribute(Type errorHandlerType) { this .errorHandlerType = errorHandlerType; } public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters) { } public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) { IErrorHandler errorHandler; errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType); foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) { ChannelDispatcher channelDispatcher = channelDispatcherBase Read More...
|
-
Back to errors and faults for a bit with this two part series on modifying the HTTP status code used for fault messages. First, we'll need some background. What happens at the HTTP level when a web service encounters a problem? That's a good question because it's not clear at all from programming the service what's going to happen under the covers. Let's build a service and find out. Here's the simplest web service I could think of that has a slight problem. I'll be hosting this in IIS so there's no other code needed to get going. You can imagine the configuration that goes along with this service, but I'm going to omit any discussion of that because it won't be relevant to anything we have to look at. [ServiceContract] public interface IService { [OperationContract(Action = "*" , ReplyAction = "*" )] Message Action(Message m); } public class Service : IService { public Message Action(Message m) { throw new FaultException( "!" ); } } We can cut all the crud out of the messages by using a POX binding: a text encoder with MessageVersion set to None and a normal HTTP transport. Now, we can run this service and see what happens. I'm just going to telnet to the service address so that we can easily see all of the HTTP headers. HTTP/1.1 500 Internal Server Error Content-Type: application/xml; charset=utf-8 Server: Microsoft-IIS/7.0 X-Powered-By: ASP.NET Date: Tue, 10 Jan 2007 06:25:16 GMT Connection: close Content-Length: 159 < Fault xmlns ="http://schemas.microsoft.com/ws/2005/05/envelope/none" >< Code >< Value > Sender </ Value ></ Code >< Reason >< Text xml:lang ="en-US" > ! </ Text ></ Reason ></ Fault > This shows us that our service fault exception results in an HTTP status code of 500 for the response. The body of the message is something that looks a lot like a SOAP fault, but smaller because we said we weren't going to use SOAP. Inside the fault message you can see the fault elements that we talked about in past articles, and it's clear that we can modify anything inside the fault. It's not clear though what we modify to alter the framing of the HTTP response. That brings us to the actual question for this pair of articles. How do I modify the HTTP status code that gets sent back with a fault? We'll answer that question next time, which is going to require writing a bit more code. Next time: Modifying HTTP Error Codes, Part 2 Read More...
|
-
The last piece of this eleven part series on fault messages covers advice for channel authors that need to define their own set of faults. Everything here assumes that you're writing a protocol channel, that you have interesting failure cases that need to be acted on programmatically, and that your protocol does not overlap an existing protocol, such as security, reliable messaging, or transactions. By now, you should either be familiar with all of the classes involved with faults or going back to read the previous articles in the series. Basics of Failure Creating Faults, Part 1 Creating Faults, Part 2 Creating Faults, Part 3 The Most Distinguished Fault A Historical, Awkwardly Named Fault Consuming Faults, Part 1 Consuming Faults, Part 2 Zen Faults Faults and HTTP Let's start with the basic definition of a SOAP fault. It's mandatory to have a fault action, fault code, and fault reason. The fault detail is optional. The fault action is the first round of filtering performed on faults and so you should define an action that is unique to your protocol. Every fault that you create for the protocol should have this same action. Following this rule helps you and everyone else quickly sort out the faults that you need to handle and let everything else go up to the next protocol layer. The fault code should be unique to each expected type of recovery action. For instance, if you have two faults that you need to programmatically handle in a different fashion, then those faults should have different fault codes. If you have two faults that are semantically the same, then those faults should have the same fault code but different fault reasons. Every fault should have its own descriptive fault reason that explains why the fault occurred and what the user should do. Fault reasons are localizable if you're translating your application into multiple languages. The final piece is the fault detail, which you should only provide if you have some extra information that hasn't been covered by one of the previous parts. Your channel should throw exceptions when an error occurs, following the standard rules for exceptions in a CommunicationObject, such as using subtypes of CommunicationException or TimeoutException. The granularity of new exception subtypes should be similar to the granularity of new fault codes. Your channel then needs to override GetProperty<FaultConverter> to provide a converter that translates back and forth between exceptions and fault messages. Your Read More...
|
-
I left HTTP error codes out of yesterday's post on zen faults because they're representative of a distinct class of out-of-band fault messages. Out-of-band faults map fault information to a transport-specific mechanism that carries the data outside of the normal message payload. Although we don't send a regulation SOAP fault message, there's still a clear concept of a message and the other side doesn't have to rely on intuition to know that a fault took place. In the case of HTTP, the first line of a response contains status information that can be used to signify an error. There is a tradeoff to using this mechanism because SOAP faults are significantly richer than HTTP status codes while HTTP status codes are much more broadly understood by devices and programs. The status codes we care about lay in the 400 and 500 ranges. Codes in the 400 range are used for client errors and codes in the 500 range are used for server errors. Code 400 represents a generic error with the client request with additional definitions for codes 401 through 417. Similarly, code 500 represents a generic error happening on the server with additional definitions for codes 501 through 505. A code 400 error would represent something like an uninterpretably mangled request message. The common code 404 (Not Found) error represents EndpointNotFound while the much less common code 415 error (Unsupported Media Type) represents an InvalidContentType for the SOAP message. A code 500 error would represent something like a catastrophic service error, such as the code for the service can't be compiled. The only other common server error is code 503 (Service Unavailable). Code 503 errors typically represent unavailability due to server load, roughly the equivalent of ServerTooBusy. Next time: Designing New Faults Read More...
|
-
I've been talking about fault messages for a while now, specifically the kind that are sent around as the body contents of a SOAP message. However, some of the most important faults are reported without sending a message at all. In that case, we have to intuit that a fault has occurred based on the other sources that we can observe. I'll cover a special case of these zen faults, faults over HTTP status codes, tomorrow. There are plenty of examples of such faults that don't involved HTTP though. How are faults transmitted without sending messages? And, why do we do this? Throughout this post I'm assuming that we're just talking about faults in the underlying system. There are plenty of ways for applications to zen fault the other side through ungraceful shutdowns or arbitrary behavior. This is especially caused by Abort, which simply disposes of all network resources without bothering to clean up after itself (certain protocol channels, such as transactions, may attempt to salvage the situation but that's another matter). The zen fault that most everyone has probably seen is an EndpointNotFound. We sometimes have to intuit that fault based on a connection being closed or refused. Occasionally, the endpoint has legitimately gone missing. Frequently though, our intuitive guess about the fault is wrong. More likely problems are an error in configuration, the presence of firewalls, or the server simply being too busy to accept the connection in time. All of these situations can make it look like a service isn't running at all. Another common zen fault is the result of a quota being exceeded. Quota faults often manifest themselves as "Remote connection aborted" type errors. The other side has decided that it has spent too many resources on this connection and doesn't want to spare any more sending back a fault message. The client has little or no indication of what went wrong as there's no way to determine why the server closed the connection. This type of fault is basically indistinguishable from an arbitrary call to Abort. In order to diagnose the fault successfully, you need to look at trace logs on the server where notice of the quota exception is recorded. You may be hinted to the presence of a quota fault by the problem occurring reliably at "round" numbers, such as 60 seconds, 65536 bytes, or 10 connections. The last type of common zen fault occurs when there's a fault prior to the caller's identity being verified (for instance, if the identity verification Read More...
|
-
Picking up from last time, we were going to look at consuming exceptions to possibly produce a fault message. The same machinery is used here as for the reverse conversion process . Exceptions go through an instance of FaultConverter, you can create your own FaultConverter in a custom channel to control the behavior, and we have a default FaultConverter for a variety of common exception types. Here's the entire interface for FaultConverter, including the two methods that we used last time for consuming fault messages. public abstract class FaultConverter { protected FaultConverter(); public static FaultConverter GetDefaultFaultConverter(MessageVersion version); protected abstract bool OnTryCreateException(Message message, MessageFault fault, out Exception exception); protected abstract bool OnTryCreateFaultMessage(Exception exception, out Message message); public bool TryCreateException(Message message, MessageFault fault, out Exception exception); public bool TryCreateFaultMessage(Exception exception, out Message message); } The two methods of interest for today are TryCreateFaultMessage and OnTryCreateFaultMessage. TryCreateFaultMessage takes in an exception and potentially produces a fault message. Override OnTryCreateFaultMessage to handle exceptions and return whether you were successful. Unlike TryCreateException, TryCreateFaultMessage does not reject a null exception argument. This means that you need to do the check yourself if needed. However, the output of TryCreateFaultMessage has the same level of screening as TryCreateException. If you claim that your FaultConverter handled the exception but don't provide a fault message, then we'll throw an InvalidOperationException. If your FaultConverter claims it didn't handle the exception but provides a fault message anyway, we'll again throw an InvalidOperationException. There are four exception types that the default fault converter will handle to produce specific fault messages. The private exception type for MustUnderstand (this will look like a CommunicationException to you) becomes a MustUnderstand fault ActionNotSupportedException becomes an ActionNotSupported fault The private exception type for ActionMismatch (this will look like a ProtocolException to you) becomes an ActionMismatch fault MessageHeaderException becomes an InvalidAddressingHeader fault if generated by a duplicate header and a MessageAddressingHeaderRequired fault otherwise ActionMismatch and MessageHeaderException require WS-Addressing Read More...
|
-
The next two episodes are about consuming fault messages and exceptions. Day one covers consuming fault messages and possibly producing an exception. Day two covers consuming exceptions and possibly producing a fault message. Both directions work by going through an instance of FaultConverter. You can override this conversion behavior in a custom channel by subclassing FaultConverter and returning an instance whenever GetProperty<FaultConverter> is called on your channel. We also have a default FaultConverter that handles a number of cases. When your channel receives a fault, there are three things you can do. If the fault message is unintelligible, you might just give up and treat the fault message as you would any other bad message. Assuming that the fault message is valid, you'll want to either handle a recoverable fault or propagate an unrecoverable fault. You'll also want to handle any fault that higher levels of the protocol stack will find unintelligible. That just leaves dealing with propagating a fault as an exception. We'll need two methods from FaultConverter to take care of this last case. public bool TryCreateException(Message message, MessageFault fault, out Exception exception); protected abstract bool OnTryCreateException(Message message, MessageFault fault, out Exception exception); TryCreateException takes in a fault message and potentially produces an exception. Override OnTryCreateException to handle fault messages and return whether you were successful. If you claim that your FaultConverter handled the fault but don't provide an exception, then we'll throw an InvalidOperationException. The same thing happens if your FaultConverter claims it didn't handle the fault but provides an exception anyway. The default fault converter can handle eight different faults in its OnTryCreateException, some of which only apply to specific addressing modes. I've talked about several of these before, but here's the complete list. MustUnderstand becomes a ProtocolException ActionNotSupported becomes an ActionNotSupportedException DestinationUnreachable becomes an EndpointNotFoundException InvalidAddressingHeader becomes a MessageHeaderException (InvalidCardinality only) or a ProtocolException MessageAddressingHeaderRequired becomes a MessageHeaderException MessageInformationHeaderRequired becomes a ProtocolException InvalidMessageInformationHeader becomes a ProtocolException EndpointUnavailable becomes a ServerTooBusyException Next time: Consuming Faults, Read More...
|
|
|
|