2012-01-12 19 views
5

Tôi quan tâm đến việc xử lý đúng các lỗi trong một máy khách WCF REST Service. Trong khi sử dụng bất kỳ WebClient, WebRequest, hoặc HttpWebRequest như vậy:Làm thế nào để xử lý/phân tích lỗi cho phần còn lại WCF được gọi là sử dụng WebClient

try 
    { 
     HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri); 
     req.Method = "GET"; 
     HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); 
     // ...process... 
    } 
    catch (WebException wex) 
    { 
     string exMessage = wex.Message; 
     if (wex.Response != null) 
     { 
     using (StreamReader r = new StreamReader(wex.Response.GetResponseStream())) 
      exMessage = r.ReadToEnd(); 

     // the fault xml is available here, really need to parse? and how? 
     } 
    } 

tôi có thể nhìn thấy trong Fiddler mà tôi đang nhận được một XML định dạng đẹp "lỗi" tin nhắn (hoặc mặc định vì includeExceptionDetailInFaults = true, hoặc một lỗi tùy chỉnh qua IErrorHandler :: ProvideFault). Tuy nhiên, chỉ có 500 lỗi nội bộ WebException đang được ném.

Tôi muốn có được một FaultException được ném trên máy khách hoặc ít nhất có thể phân tích lỗi. Chúng tôi không sử dụng "Dịch vụ tham khảo" vì vậy không có proxy (xin vui lòng sửa tôi nếu có một cách tốt hơn để làm điều này cho một khách hàng WCF REST). Có cách nào chung để phân tích lỗi đó bất kể loại T thực tế của nó (FaultException) hay thậm chí đối với loại cụ thể làm điểm bắt đầu? Cảm ơn!

Dựa trên câu trả lời từ degorolls:

public SomeContract ThrowErrorTest() 
{ 
    try 
    { 
     return TryCatchExtractAndRethrowFaults<SomeContract>(() => 
     { 
      // Call web service using WebClient, HttpWebRequest, etc. 
      return SomeContract; 
     });     
    } 
    catch (FaultException<CustomFault> fexCustom) 
    { 
     Dbg.WriteLine(fexCustom.Message); 
    } 
    catch (FaultException fex) 
    { 
     Dbg.WriteLine(fex.Message); 
    } 
    catch (WebException wex) 
    { 
     Dbg.WriteLine(wex.Message); 
    } 
    catch (Exception ex) 
    { 
     Dbg.WriteLine(ex.Message); 
    } 
    return null; 
}   

static public T TryCatchExtractAndRethrowFaults<T>(Func<T> doWebRequest) 
{ 
    try 
    { 
     return doWebRequest(); 
    } 
    catch (WebException wex) 
    { 
     FaultException fe = ConvertWebExceptionIntoFault(wex); 
     if (fe != null) 
      throw fe; 
     throw;  // not a fault, just re-throw 
    } 
} 

static protected FaultException ConvertWebExceptionIntoFault(WebException wex) 
{ 
    if (wex.Response == null) 
     return null; 

    XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(
     wex.Response.GetResponseStream(), 
     new XmlDictionaryReaderQuotas()); 

    Message msg = Message.CreateMessage(MessageVersion.None, "ParseFaultException", xdr); 

    // If the start element of the message is "Fault" convert it into a FaultException 
    // 
    using (MessageBuffer msgBuffer = msg.CreateBufferedCopy(65536)) 
     using (Message msgCopy = msgBuffer.CreateMessage()) 
      using (XmlDictionaryReader reader = msgCopy.GetReaderAtBodyContents()) 
       if (reader.IsStartElement("Fault")) 
       { 
        // Must make a copy for the converter 
        msg.Close(); 
        msg = msgBuffer.CreateMessage(); 
        return ConvertMessageToFault(msg); 
       } 

    return null; 
} 

static FaultException ConvertMessageToFault(Message msg) 
{ 
    EnvelopeVersion ev = msg.Version.Envelope; 
    var fault = MessageFault.CreateFault(msg, 65536); 

    if (fault.HasDetail) 
    { 
     string faultName = fault.GetReaderAtDetailContents().Name; 
     switch (faultName) 
     { 
      case "ExceptionDetail": // handle the default WCF generated fault 
       ExceptionDetail exDetail = fault.GetDetail<ExceptionDetail>(); 
       return new FaultException<ExceptionDetail>(exDetail, fault.Reason, fault.Code); 

      case "CustomFault":  // handle custom faults 
       CustomFault cstmDetail = fault.GetDetail<CustomFault>(); 
       return new FaultException<CustomFault>(cstmDetail, fault.Reason, fault.Code); 

      default: 
       throw new Exception("Unrecognized fault detail '" + faultName + 
            "' while re-constructing fault."); 
     } 
    } 
    return null; 
} 

Trả lời

6

Lỗi là một phần của giao thức SOAP và không có sẵn trong kịch bản REST. Tôi không tin rằng bất kỳ cơ sở hạ tầng WCF hỗ trợ những gì bạn đang làm ra khỏi hộp.

Bạn có thể đặt FaultExceptionEnabled = true trong cấu hình hành vi WebHttp của bạn để nhận lỗi FaultException thay vì 500.

Tuy nhiên, bạn cũng có thể làm điều gì đó như thế này (Tôi đã thực hiện việc này trong một số trường hợp thử nghiệm). Phương pháp này dựa vào việc biết trước thời gian loại lỗi FaultDetail mong đợi trong lỗi.

 bool isFault; 
     if (message.Version == MessageVersion.None) 
     { 
      //Need to determine for ourselves if this is a fault; 
      using (MessageBuffer buffer = message.CreateBufferedCopy(65536)) 
      { 
       message.Close(); 
       message = buffer.CreateMessage(); 
       using (Message message2 = buffer.CreateMessage()) 
       { 
        using (XmlDictionaryReader reader = message2.GetReaderAtBodyContents()) 
        { 
         isFault = reader.IsStartElement("Fault", "http://schemas.microsoft.com/ws/2005/05/envelope/none"); 
        } 
       } 
      } 
     } 
     else 
     { 
      // For SOAP messages this is done for us 
      isFault = message.IsFault; 
     } 

     if (isFault) 
     { 
      var fault = MessageFault.CreateFault(message, 65536); 
      MyServiceFault detail = null; 
      if (fault.HasDetail) 
      { 
       // The only thing we can possible have as detail is an MyServiceFault 
       detail = fault.GetDetail<MyServiceFault>(); 
      } 
      FaultException ex = new FaultException<MyServiceFault>(detail, fault.Reason, fault.Code); 
      throw ex; 
     } 
+0

Điều đó dường như khiến tôi gần gũi. Trên MessageFault.CreateFault() tôi nhận được khiếu nại innerException về không gian tên không phải là ".../soap-envelope" thay vì ".../envelope/none". Biết cách tốt để giải quyết vấn đề không gian tên? Nó thực sự đến từ máy chủ là ".../envelope/none". – crokusek

+0

Tôi đã thay đổi đối số thành Message.CreateMessage() thành MessageVersion.None thay vì MessageVersion.Default và đã khắc phục sự cố không gian tên. Vì tôi bắt đầu với một WebException đã kết thúc việc tạo thông báo từ luồng wex.Response. Sẽ đăng những gì đã kết thúc làm việc ở cuối câu hỏi. – crokusek

+0

Bạn có nhận được WebException ngay cả sau khi đặt FaultExceptionEnabled = true không? –

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