FaultException.Detail coming back empty
Asked Answered
P

5

10

I am trying to catch a given FaultException on a WCF client. I basically need to extract a inner description from the fault class so that I can then package it in another exception for the upper layers to do whatever.

I've done this successfully a number of time, what makes it different this time is that fault is declared as an array, as you can see from the service reference attribute declared on top of the method that throws the exception:

[System.ServiceModel.FaultContractAttribute(typeof(FaultClass[]), Action = "http://whatever/", Name = "whateverBusinessFault")] 

This is my code:

try
{
  // call service here
}
catch (FaultException<FaultClass[]> ex)
{
  if (ex.Detail != null && ex.Detail.Length > 0)
  {
    throw new CustomException(ex.Detail[0].description);
  }
  else
  {
    throw;
  }
}

Problem is Detail (which is an array) is always coming back empty in the code even if I can see the data (description field etc.) in the SOAP response from WCF trace.

So the stuff I need is definitely coming back but for some reason either it doesn't get deserialized or I can't get to it from code.

Any help appreciated!

UPDATE:

Trying with @Darin suggestion but no luck, the string I am extracting from the XmlReader is "/r/n":

var sb = new StringBuilder();

using (XmlReader reader = fault.GetReaderAtDetailContents())
{
  while (reader.Read())
     sb.AppendLine(reader.ReadOuterXml()); 
}

var detail = sb.ToString();

Looks like the detail section is not coming up at all!

Phobia answered 8/9, 2010 at 16:38 Comment(4)
What happens if you modify your code (just for test) to use only FaultClass (not array)?Pawnshop
already tried that, it doesn't catch the exception at allPhobia
This question can't be answered without either a sample fault response or the actual schema of the custom fault defined in the WSDLDisannul
I had this issue. The wsdl used to code gen the classes, described the details section as an Array. But the array when caught in the exception was always empty. When I viewed the xml in fiddler I noticed there was a node for the details section, then a child node and inside that child node was the array. So there was an extra node between the details section and the array. I created a new class for this extra node and I added a property to contain the array. I then went to where the FaultContractAttribute was declared and I swapped in my class as the Type (T).Bordeaux
P
11

I found the solution on a UPS Forum :

https://developerkitcommunity.ups.com/index.php/Special:AWCforum/st/id371

"The problem was the visual studio didn't quite map out the ErrorDetail objects right. The ErrorDetail node is called "ErrorDetail", but the type generated for it is "ErrorDetailType." I edited the reference.cs class generated for each service I was using and added a TypeName:"

Pyromagnetic answered 15/9, 2016 at 17:35 Comment(4)
My hero! :) To clarify, he's talking about the XmlTypeAttribute, add TypeName = "ErrorDetail" there and it'll work.Cancellation
Thank you, this was very helpful. I'm not sure if the developer kit community URL is still valid.Hydraulic
I believe this is fixed in the latest UPS WS WSDL. Just use the Rate.Errors node: catch (FaultException<Rate.Errors> errorsFault)Fulgurate
where to add TypeName = "ErrorDetail"?Weathertight
K
5

It is difficult to say where the problem is but I suspect the smoking gun is this axis web service not generating standard message. One way to workaround this would be to parse the XML yourself:

try
{
    proxy.CallSomeMethod();
}
catch (FaultException ex)
{
    var fault = ex.CreateMessageFault();
    using (XmlReader reader = fault.GetReaderAtDetailContents())
    {
        // TODO: read the XML fault and extract the necessary information.
    }
}
Katzman answered 22/9, 2010 at 18:36 Comment(4)
I think we're getting closer! problem is I don't know how that detail is formed so don't know which elements/attributes I should be looking for. Is it gonna look exactly like the response that I see in the soap that comes back from the service?Phobia
(for some reason SO doesn't let me upvote you, says I already voted which is not the case)Phobia
@Johnldol, yes it will look the same as the response you got from SoapUI.Katzman
OK - no luck. See update, I am simply putting the detail from the xmlReader in a string (to see what it looks like) and all is there is a "/r/n". Looks like it's coming up empty this way too :(Phobia
D
3

It took me ages to figure out how to get the full details message from a FaultException as a string. I eventually figured it out and wrote this extension method:

public static string GetDetail(this FaultException faultException)
{
    if (faultException == null)
        throw new ArgumentNullException(nameof(faultException));

    MessageFault messageFault = faultException.CreateMessageFault();
    if (messageFault.HasDetail) {
        using (XmlDictionaryReader reader = messageFault.GetReaderAtDetailContents()) {
            return reader.ReadContentAsString();
        }
    }
    return null;
}

Originally I was using reader.Value but that only appeared to the return the first line of a multi-line details message. reader.ReadContentAsString() appears to get the whole thing, new lines included, which is what I wanted.

Degas answered 24/8, 2016 at 16:7 Comment(1)
I've noticed this works well if details is a multiline string, but not so well if it is a XML element hierarchy.Degas
M
2

I came up with the simplest test case I could. I hope it will help you. Server side:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [FaultContract(typeof(FaultClass[]))]
    string Crash();
}

public class Service1 : IService1
{
    public string Crash()
    {
        var exception = new FaultException<FaultClass[]>(new FaultClass[] { new FaultClass { Data = "TEST" } }, new FaultReason("Boom"));

        throw exception;
    }
}

[DataContract]
public class FaultClass
{
    [DataMember]
    public string Data { get; set; }
}

Client side:

try
{
    using (var client = new Service1Client())
    {
        client.Crash();
    }
}
catch(FaultException<FaultClass[]> e)
{
    //Break here
}
Munro answered 17/9, 2010 at 6:0 Comment(3)
Thanks, I have no access the service itself (it's a java axis service). I am consuming with a WCF client and it does catch the fault but the array is empty, even though I can see (from the trace) there is one item in the response from the service.Phobia
It may be due to a difference in a XML namespace that would prevent the data in the Fault from being deserialized. Did you compare the content of the FaultClass from WCF with the content of a FaultClass from Java?Munro
the only thing I have is the wsdl, from which I generate the WCF service reference. I compared the content of the wsdl to the generated service reference and it looks ok. If this were to be case (namespace problems) I would expect to have deserialization issues on other types too as faults are in the same namespace as the rest of the typesPhobia
C
1

I had a similar situation in trying to communicate data with faults (specifically a stack trace). See this question. I ended up solving it by creating my own serializable stack trace and including it in a derived FaultException class.

Casanova answered 25/4, 2011 at 3:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.