|
|
Browse by Tags
All Tags » Faults » Answers (RSS)
-
How do I create a fault with multiple child nodes for the detail element? All of the overloads of CreateFault take a single argument for detail, which lets me build a tree of nodes but not a forest. The trick here is not to get hung up on trying to make the built-in convenience methods work. The object model for all of the message classes is just a wrapper around a way to interact with XML readers and writers. Notice that MessageFault is an abstract class. You can create your own subclasses to directly work against the underlying reader and writer. Also notice that there are only four methods that you have to override to make a MessageFault: Code, Reason, HasDetail, and OnWriteDetailContents. Code and Reason are parameters that you would have had to pass to CreateFault anyway. If you care about how the contents of the detail element are structured, then you definitely have a detail element. That really just leaves OnWriteDetailContents to implement, which is exactly what you wanted to precisely control the contents of the detail element. class MyMessageFault : MessageFault { FaultCode code; FaultReason reason; public MyMessageFault(FaultCode code, FaultReason reason) { this .code = code; this .reason = reason; } public override FaultCode Code { get { return this .code; } } public override bool HasDetail { get { return true ; } } protected override void OnWriteDetailContents(XmlDictionaryWriter writer) { // You can write whatever XML you want here } public override FaultReason Reason { get { return this .reason; } } } On the reverse side, you'll have exactly the same problem with GetDetail because it too is limited to a single object. In this case though, you have the replacement directly accessible, GetReaderAtDetailContents, and are more likely to already have been using it. Next time: Default ProtectionLevel for Standard Bindings Read More...
|
-
How can I directly craft the XML content that goes into a fault detail? Getting control over the detail element doesn't have to mean crafting the fault message yourself. While WCF requires that the fault detail be serializable using a data contract, remember that DataContractSerializer treats XmlElement as a special primitive type. This allows you to construct arbitrary content using XmlElement when your content can be represented as a rooted document. Due to the automatic conversion process of FaultException to a fault message, you don't need to construct a data contract to act as a wrapper. Here's a sample that builds some content in an XmlElement and uses it to construct a fault. I made the method return a Message so that I could look at the response more easily but you can use any contract you want. [ServiceContract] public interface IMyService { [OperationContract] Message Fail(); } public class MyService : IMyService { public Message Fail() { XmlDocument document = new XmlDocument(); XmlElement root = document.CreateElement( "tag" ); root.SetAttribute( "attributeName" , "value" ); XmlElement subtag = document.CreateElement( "moretags" ); subtag.InnerText = "blah" ; root.AppendChild(subtag); throw new FaultException<XmlElement>(root); } } public class Program { static void Main( string [] args) { string address = "http://localhost:8000/" ; BasicHttpBinding binding = new BasicHttpBinding(); ServiceHost host = new ServiceHost( typeof (MyService), new Uri(address)); host.AddServiceEndpoint( typeof (IMyService), binding, "" ); host.Open(); ChannelFactory<IMyService> factory = new ChannelFactory<IMyService>(binding); IMyService proxy = factory.CreateChannel( new EndpointAddress(address)); Message response = proxy.Fail(); Console.WriteLine(response.ToString()); Console.ReadLine(); host.Close(); } } That code produces a fault message that looks like the following. < s:Envelope xmlns:s ="http://schemas.xmlsoap.org/soap/envelope/" > < s:Header /> < s:Body > < s:Fault > < faultcode > s:Client </ faultcode > < faultstring xml:lang ="en-US" > The creator of this fault did not specify a Reason. </ faultstring > < detail > < tag attributeName ="value" > < moretags > blah </ moretags > </ tag > </ detail > </ s:Fault > </ s:Body > </ s:Envelope > Depending on how much hand-crafting you want, XmlDocument lets you simplify the process even further. For Read More...
|
-
When the service sends a fault message with a large detail, my client is unable to read the fault. Changing the standard settings for the maximum message size doesn't help. How can I read large fault messages? Fault messages have their own special quota that can be configured on the client proxy. For changing the settings of a proxy, you should immediately think about using a behavior. Luckily, that happens to work in this case. There is a MaxFaultSize property on the ClientRuntime, which we can get access to by supplying an IEndpointBehavior. public class SetMaxFaultSizeBehavior : IEndpointBehavior { int size; public SetMaxFaultSizeBehavior( int size) { this .size = size; } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MaxFaultSize = size; } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } } Let's use our new behavior to play with fault sizes. I've got a service that sends back a fault with no content, but we still have to deal with however big the rest of the fault message is. Then, the client will try connecting to the service with different maximum fault sizes to see what sizes work and what sizes don't. [ServiceContract] public interface IService { [OperationContract] void Operation(); } public class Service : IService { public void Operation() { throw new FaultException< string >( "" ); } } class Program { static void Main( string [] args) { Binding binding = new BasicHttpBinding(); string uri = "http://localhost:8000/" ; ServiceHost host = new ServiceHost( typeof (Service)); host.AddServiceEndpoint( typeof (IService), new BasicHttpBinding(), uri); host.Open(); int maxFaultSize = 1; while ( true ) { ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri)); factory.Endpoint.Behaviors.Add( new SetMaxFaultSizeBehavior(maxFaultSize)); factory.Open(); IService proxy = factory.CreateChannel(); try { proxy.Operation(); } catch (FaultException fault) { Console.WriteLine( "Received fault with maxFaultSize={0}." , maxFaultSize); break ; } catch (QuotaExceededException exception) { Console.WriteLine( "Exceeded quota with maxFaultSize={0}." , maxFaultSize); } finally { factory.Close(); } maxFaultSize++; } host.Close(); Console.ReadLine(); 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...
|
|
|
|