WCF Troubleshooting (2)

WCF 2010. 11. 29. 09:00 Posted by 알 수 없는 사용자
일주일 만에 돌아온 RuAA 입니다. 하핫~
이제 곧 올해의 마지막인 12월이네요~ 어느새,, 벌써,, ㅡㅠ
나이만 먹는 것 같아 참 슬퍼지려 합니다.
12월엔 술자리도 많고, 행사도 많아 바빠지는데, 모두 건강 챙기시길 바랍니다.


다른 설명 없이 바로~ 지난 포스트에 이어서, 진행해보도록 하겠습니다~

이번 포스팅에서는 FaultContractAttribute 대해서 잠깐 알아보고, 이를 이용하여 에러 메시지를 직접 정의해보도록 하겠습니다.

그럼, FaultContractAttribute 무엇이냐~? 간단하게아주 간단하게직접 정의한 에러 메시지를 클라이언트로 전달하기 위한 서비스 메서드의 속성이라고 생각하면 됩니다.

닷넷에는 기본적으로 제공하는 여러 종류의 예외 클래스가 존재합니다. 지난 포스트에서 봤듯이 이러한 예외가 발생하였을 때는 그에 맞는 예외 클래스에 정의되어 있는 메시지들이 클라이언트로 전달되었습니다
.
하지만, 이러한 메시지들 이외에
서비스 정책에 맞는 메시지를 따로 정의하고, 이를 클라이언트에 노출하고 싶을 , FaultContractAttribute 사용할 있습니다.

그럼, 이를 이용해,  특정 에러 메시지를 정의하고, 메시지를 클라이언트에 전달하는 예제를 한번 보도록 하겠습니다. 예제 한번 보고 나면 이해가 되실겁니다. 하하

우선, WCF 서비스에서 사용할 있는 새로운 클래스를 정의합니다. 물론, DataContractAttribute 이용해서요~


[
DataContract]

public class ErrorInfo

{
    [
DataMember]

    public string Info { get; set; }

 

    [DataMember]

    public ErrorCode Code { get; set; }

}

 

[DataContract]

public enum ErrorCode

{

    [EnumMember]

    WrongName,

    [EnumMember]

    NotExist

}

 


ErrorInfo 라는 이름의 클래스를 정의 하였습니다. 클래스에는 에러의 정보를 담을 있는 Info 라는 프로퍼티가 있고, 에러의 종류를 나타내는 Code 라는 프로퍼티가 정의되어 있습니다. 또한, 덤으로 에러 종류를 쉽게 분류하기 위하여 ErrorCode 라는 이름의 열거형을 정의하였습니다.

그럼, FaultContractAttribute 어디에 정의하는 걸까요? 다음 코드를 보시면 바로 있습니다~


[
ServiceContract]

public interface IService1

{

[OperationContract]

int Divide(int numerator, int denominator);

 

    [OperationContract]

    [FaultContract(typeof(ErrorInfo))]

    int FindEmployee(string employeeId);

}

 


보이시죠? ~ 바로 OperationContractAttribute 같은 위치에 선언됩니다. 예제에서 FindEmployee 오퍼레이션은 ErrorInfo 타입의 에러 메시지가 발생할 있다는 것을 나타냅니다.

이제, 직접 ErrorInfo 클래스에 에러 메시지를 정의하고, 클라이언트로 전달하는 코드를 보셔야죠~
다음 예제를 보겠습니다.


public
class Service1 : IService1

{

    public int Divide(int numerator, int denominator)

    {

        return numerator / denominator;

    }

 

    public int FindEmployee(string employeeId)

    {

        // 사용자 정의 에러 발생

        FaultReason reason = new FaultReason(string.Format("{0} employee is not exist", employeeId));

        ErrorInfo error = new ErrorInfo

        {

            Info = string.Format("Not Exist Employee, ID : {0}", employeeId),

            Code = ErrorCode.NotExist

        };

 

        throw new FaultException<ErrorInfo>(error, reason);

    }

}

 


FindEmployee 메소드는 무조건 예외를 발생하도록 되어 있습니다. 사실, 에러 메시지를 클라이언트로 전달하는 것이 목적이니깐 다른 코드는 예제에서 생략이 되어도 상관없겠죠~ ^^

FaultReason 이라는 새로운 클래스도 눈에 띄는군요~ 클래스는 해당하는 오류의 간단한 메시지를 작성하기 위한 클래스라고 생각하시면 됩니다.

그리고, 방금 만들었던 ErrorInfo 인스턴스를 생성하고, Info, Code 프로퍼티에 클라이언트로 전달하고 싶은 오류 메시지를 입력하였습니다.
마지막으로 FaultException<T> 이용하여 ErrorInfo 포함한 예외를 발생시켰습니다
.
FaultException
클래스는 SOAP 오류로 변환될 있는 예외 클래스입니다.

이제, 클라이언트에서 에러메시지를 낚아채어(?) 보여주는 예제를 보겠습니다.
콘솔 어플리케이션을 생성하고, 서비스 참조를 후에 다음과 같이 코드를 작성 해보았습니다.


static
void Main(string[] args)

{

    Service1Client proxy = new Service1Client();

 

    try

    {

        proxy.Divide(5, 0);

    }

    catch (FaultException ex)

    {

        Console.WriteLine("Reason : {0}", ex.Reason.ToString());

        Console.WriteLine("Message : {0}", ex.Message);

        Console.WriteLine();

    }

 

    try

    {

        proxy.FindEmployee("RuAA");

    }

    catch (FaultException<ErrorInfo> ex)

    {

        Console.WriteLine("Reason : {0}", ex.Reason.ToString());

        Console.WriteLine("Message : {0}", ex.Message);

        Console.WriteLine("\n<< Detail Info >>");

        Console.WriteLine("Code : {0}", ex.Detail.Code);

        Console.WriteLine("Info : {0}", ex.Detail.Info);

    }

}

 


처음엔 Divide 오퍼레이션을 호출하여 닷넷에서 기본적으로 제공하는 예외를 발생시켰고, 번째 try, catch 문에서 FindEmployee 오퍼레이션을 호출 하였습니다.

위의 코드에서 보듯이, 클라이언트에서 서비스의 예외를 낚아채기(?) 위해선 FaultException 클래스를 사용합니다. FaultException 클래스에 제네릭 형식이 정의되어 있지 않은 경우엔 닷넷에서 제공하는 기본적인 예외 메시지를 catch 있으며, 서비스에 정의 특정 예외 메시지를 catch 하고자 , FaultExcepton 클래스에 제네릭 형식을 지정해주면 됩니다.

그리고, 가지 주목할 점은, ErrorInfo 인스턴스가 FaultException 인스턴스의 Detail 프로퍼티에 입력된다는 것입니다. 이는, 뒤에 보여줄 SOAP Fault 메시지를 확인하면 아마 이해가 쉬우실겁니다.
때문에, (서비스 오퍼레이션에서 정의했던
) ErrorInfo 클래스의 Info, Code 프로퍼티에 입력된 값을 가져오기 위해선 ex.Detail.Code(또는 ex.Detail.Info) 같이 접근 하여야 합니다.

이렇게 처리를 하면, 다음과 같은 클라이언트 결과를 확인할 있습니다.


마지막으로, 서비스에서 클라이언트로 전달되는 SOAP 메시지를 확인해 보겠습니다.

 

<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/IService1/FindEmployeeErrorInfoFault</a:Action>

    <a:RelatesTo>urn:uuid:c67dd581-4b6d-4a02-aa35-fba3515a0ccf</a:RelatesTo>

  </s:Header>

  <s:Body>

    <s:Fault>

      <s:Code>

        <s:Value>s:Sender</s:Value>

      </s:Code>

      <s:Reason>

        <s:Text xml:lang="en-US">RuAA employee is not exist</s:Text>

      </s:Reason>

      <s:Detail>

        <ErrorInfo xmlns="http://schemas.datacontract.org/2004/07/Wcf_TroubleShooting"

                   xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

          <Code>NotExist</Code>

          <Info>Not Exist Employee, ID : RuAA</Info>

        </ErrorInfo>

      </s:Detail>

    </s:Fault>

  </s:Body>

</s:Envelope>

 


SOAP 메시지를 확인해 보면 ErrorInfo 라는 엘리먼트가 눈에 ~!! 띄는군요. 하하
이렇게 SOAP 메시지의 내용과 앞의 예제 코드, 그리고 클라이언트의 결과 화면을 비교해보면 이번 포스팅의 내용이 이해하기 쉬울 같습니다. ^^

아직 얘기가 많지만, 다음 포스팅을 기약하면서, 마무리 하도록 하겠습니다.