해가 바뀌고, 벌써 두 달이 거의 지나가고 있는 시점에 이제야 다시 글을 올리게 되었습니다.
참, 부끄럽기 그지 없군요~ ^^;;
부끄럽긴 하지만 인사는 드려야죠!! 새해 복!! 많이 받으쌉싸리와용~
Method | Description |
HandleError | 예외에 대해 어떤 공통적인 동작을 취할 수 있는 메서드입니다. (예 : 예외 정보 로깅) |
ProvideFault | 클라이언트로 보내질 fault message 를 정의할 수 있는 메서드입니다. |
아하~ 그리 어렵지 않죠? ㅎ
그럼, 직접 한번 구현 해보겠습니다.
namespace ErrorHandler
{
[DataContract]
public class MyErrorInfo
{
[DataMember]
public string Message { get; set; }
[DataMember]
public string ExceptionInfo { get; set; }
}
public class SampleErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
// 이곳에 발생한 예외에 대한 공통적인 작업을 구현할 수 있습니다.
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultReason reason = new FaultReason("내맘대로 무조건 예외");
FaultException<MyErrorInfo> faultException = new FaultException<MyErrorInfo>(
new MyErrorInfo
{
Message = error.Message,
ExceptionInfo = error.ToString()
},
reason);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(
version,
messageFault,
faultException.Action);
}
}
이 예제에서는 HandleError 메서드에 별 다른 코드를 넣지 않았습니다. 앞에서 언급을 했었지만 HandleError 메서드에서는 발생한 예외에 대한 어떤 공통적인 행동에 대한 코드를 구현 해주시면 됩니다.
그리고, HandleError 메서드에서는 bool 형의 값을 반환 해주는 것이 보이네요~ 이 bool 형의 값은 예외가 적절하게 처리되었는지 여부를 나타내준다고 생각하시면 됩니다. 만약, false를 반환하게되면, 예외가 처리되지 않은 것으로 간주하고, 기본 응답이 사용됩니다. 이 경우 디스패처가 모든 세션을 중단하고, InstanceContext를 중단합니다.
MSDN의 HandleError 메서드 설명 페이지에도 나와 있지만, HandleError 메서드는 여러 다른 위치에서 호출될 수 있기 때문에 이 메서드에서 예외를 제대로 처리하지 못했다고 false를 반환하게 되면, 모든 상태가 손상된 것으로 간주되고, 서비스에 존재하는 모든 세션이 중단된다고 생각하시면 됩니다.
따라서, 예외가 발생했을 때 모든 세션이 중단되길 원치 않는다면 위 예제 코드처럼 true를 반환하는 것이 나을 것입니다.
ProvideFault 메서드의 코드도 그리 어려워 보이지 않는군요. 이 전 저의 포스팅을 보셨던 분이라면 같은 생각을 하실 것 같네요~ ^^
ProvideFault의 매개 변수에 대한 설명은 다음과 같습니다.
Parameter | Description |
error | 서비스 작업 중에 던져지는 Exception 개체입니다. |
version | 메시지의 SOAP 버전입니다. |
fault | 클라이언트로 보내지는 Message 개체입니다. |
위 예제 코드에선 ProvideFault 메서드 안에서 매개 변수로 받은 예외 개체(error)를 이용하여 사용자 정의 된 예외 메시지를 정의합니다. 이렇게 정의 된 예외 메시지를 매개 변수 fault에 할당만 해주면 이 메시지는 클라이언트로 전달 됩니다.
FaultException 와 FaultReason 에 관한 내용은 이 전 포스트를 확인 해주세요~ ^^
이제 ErrorHandler 구현은 끝이 났습니다.
이렇게 만든 ErrorHandler를 사용하기 위해서 다음으로 해야 할 것은 WCF 서비스에 사용할 수 있는 새로운 Behavior 를 만드는 것입니다.
Custom Behavior 를 만들어 WCF를 확장하는 방법에 대해선 이 곳을 참고하시면 좋을 것 같습니다.
그럼 새로운 Behavior를 만들어 볼까요?
public sealed class ErrorBehaviorAttribute : Attribute, IServiceBehavior
{
private List<Type> _errorHandler;
public List<Type> ErrorHandlerType
{
get { return _errorHandler; }
}
public ErrorBehaviorAttribute(params Type[] errorHandler)
{
this._errorHandler = new List<Type>();
foreach (var item in errorHandler)
{
_errorHandler.Add(item);
}
}
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
List<IErrorHandler> errorHandler = new List<IErrorHandler>();
ErrorHandlerType.ForEach(
(item) =>
{
errorHandler.Add((IErrorHandler)Activator.CreateInstance(item));
});
foreach (ChannelDispatcherBase dispatcherBase in
serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher dsp = dispatcherBase as ChannelDispatcher;
errorHandler.ForEach(
(item) =>
{
dsp.ErrorHandlers.Add(item);
});
}
}
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}
이 코드를 보면 조금 복잡해 보일 것 같습니다. Behavior의 경우 설정파일(.config)에서 설정을 하거나 코드 상에서 설정을 할 수 있는데 서비스 클래스의 Attribute 특성을 사용하여 설정합니다.
그래서 이 클래스는 Attribute 시스템 클래스를 상속합니다. 또한, IServiceBehavior 인터페이스를 상속하여 Behavior 로 사용할 수 있는 클래스를 만듭니다.
위 클래스의 생성자를 보면 하나 이상의 클래스 타입을 매개 변수로 받습니다. 이렇게 받은 타입들을 List<Type> 인스턴스의 전역 변수에 저장을 합니다. 따로 예외 처리를 하진 않았지만 생성자에 매개변수로 넘겨주는 클래스 타입은 반드시 IErrorHandler 인터페이스를 구현한 타입이어야 합니다.
그리고, IServiceBehavior 인터페이스 메서드 중에 ApplyDispatchBehavior 메서드를 구현합니다. 이 메서드에서는 서비스에 존재하는 모든 channel dispatcher 에게 생성자에서 받았던 IErrorHandler 타입들의 인스턴스를 추가시켜줍니다.
말이 조금 어렵나요? ^^;;
참고로, channel dispatcher 는 WCF 서비스를 구현할 때 어떤 Binding을 사용하는냐에 따라 달라집니다. 물론 사용하는 Binding 의 수에 따라 disptcher의 수도 늘어가게 됩니다.
dispatcher에 대해 잘 알지 못하는 분들이 있을 것 같습니다. 정확한 설명을 이 포스팅에서 하기에는 이것 만으로도 내용이 길어질 것 같아 설명하긴 힘들지만, 이 곳(디스패처 확장)의 내용을 확인하시면 이해는 가시리라 생각합니다. ^^
이제 준비 작업은 모두 끝이 났습니다. 앞에서 만든 ErrorHandler 를 사용할 수 있을 것 같네요
ErrorHandler를 사용하기 위해 다음과 같이 간단한 서비스를 만들고 서비스 클래스에 ErrorBehavior 특성을 설정하였습니다.
// Service Contract
[ServiceContract]
public interface ICalc
{
[OperationContract]
[FaultContract(typeof(MyErrorInfo))]
int Add(int a, int b);
[OperationContract]
[FaultContract(typeof(MyErrorInfo))]
int Sub(int a, int b);
[OperationContract]
[FaultContract(typeof(MyErrorInfo))]
int Mul(int a, int b);
[OperationContract]
[FaultContract(typeof(MyErrorInfo))]
int Div(int a, int b);
}
// Service 구현 클래스
[ErrorBehavior(typeof(SampleErrorHandler))]
public class Calculator : ICalc
{
public int Add(int a, int b)
{
throw new InvalidOperationException("잘못된 Add 메서드 호출입니다.");
}
public int Sub(int a, int b)
{
throw new InvalidOperationException("잘못된 Sub 메서드 호출입니다.");
}
public int Mul(int a, int b)
{
throw new InvalidOperationException("잘못된 Mul 메서드 호출입니다.");
}
public int Div(int a, int b)
{
throw new InvalidOperationException("잘못된 Div 메서드 호출입니다.");
}
모든 메서드를 호출하면 아~무 이유없이 예외를 던지고 있군요~!! ㅎ
이제 이 서비스를 빌드하고 테스트를 해보야겠죠.
이번에는 따로 Console 어플리케이션을 만들지 않고 WcfTestClient.exe를 사용해보도록 하겠습니다.
이 간단한 프로그램은 WCF 서비스를 테스트하기 위한 클라이언트 툴입니다.
Visual Studio 명령 프롬프트를 실행시키고 "WcfTestClient"를 치고 엔터를 클릭하면 실행시킬 수 있습니다. 또는 Visual Studio 에서 WCF 서비스 프로젝트를 F5 를 이용하여 실행해도 역시 이 툴을 사용할 수 있습니다.
이 툴을 실행시키면 다음과 같은 모습을 하고 있죠.
여기에서 간단히 호출하고자 하는 메서드를 왼쪽 창에서 마우스로 더블 클릭 "톡! 톡!" 해주시면 실행할 수 있습니다. 이건 너무 직관적인거라 자세한 설명을 하지 않더라도 모두 사용하실 수 있으실겁니다 ^^
아무 메서드를 하나 실행시키면 예외가 발생했다는 내용을 담고 있는 창이 뜨는 것을 보실 수 있습니다. 여기서 오류정보를 보면 위에 SampleErrorHandler 의 ProvideFault 메서드에서 정의한 내용들이 들어가 있는 것을 확인할 수 있습니다.
더 자세한 내용을 보고 싶다면, 예외 창을 닫고 오른쪽 창 밑에 있는 "XML" 탭을 클릭해보세요~ 그럼 다음과 같은 화면을 보실 수 있으실 겁니다.
응답에 있는 XML을 다시 보여드려볼까요?
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="ko-KR">내맘대로 무조건 예외</faultstring>
<detail>
<MyErrorInfo xmlns="http://schemas.datacontract.org/2004/07/ErrorHandler" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ExceptionInfo>
System.InvalidOperationException: 잘못된 Sub 메서드 호출입니다.
위치: ErrorHandler.Calculator.Sub(Int32 a, Int32 b) 파일 D:\Dev\Learning\WCF\ErrorHandler\ErrorHandler\Service1.svc.cs:줄 21
위치: SyncInvokeSub(Object , Object[] , Object[] )
위치: System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
위치: System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
위치: System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
</ExceptionInfo>
<Message>잘못된 Sub 메서드 호출입니다.</Message>
</MyErrorInfo>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
네~ XML 내용을 확인하니 좀 더 확실해 졌네요. 클라이언트가 받은 메시지에 ProvideFault 메서드에서 정의한 내용들이 들어가 있다는 것을요~ ㅎ
자~ 그럼 마무리 하겠습니다!!
이번 내용은 뭔가 조금 복잡했던 것 같지만 사실 그렇게 복잡하지 않습니다. 이 포스트를 찬찬히 되새기면서, 그리고 인터넷을 통해 다른 부가적인 내용들도 알아가면서 학습을 하시면 그리 어렵지 않다는 것을 느끼게 되실겁니다.
예외를 처리하는 방법은 실무에 꽤 많이 쓰일 수 있는 내용이니깐 제대로 알고 가는건 좋을 것 같습니다.
다음 포스팅에선 계속해서 Troubleshooting 에 관한 내용으로 찾아 뵙도록 하겠습니다.
WCF에서 제공하는 몇 가지 툴들이 있는데 이런 툴들에 대한 설명이 될 것 같습니다.
그럼 다음 포스팅때까지 안녕히~ ^^
'WCF' 카테고리의 다른 글
REST 서비스 템플릿 (3) | 2011.05.02 |
---|---|
WCF Troubleshooting (2) (2) | 2010.11.29 |
WCF Service Configuration Editor (0) | 2010.11.24 |
WCF Troubleshooting (1) (0) | 2010.11.19 |
WCF Security (2) - 전송 계층에서의 메세지 인증 (사용자 지정 인증) (0) | 2010.09.06 |