어떤 분께서 질문하셔서 간단한 예제를 작성해 봤습니다. 다른 분들께서도 유용하실 것 같아서 알려드립니다.

HTTP 기반 WCF 서비스에서의 쿼리 스트링

HTTP 기반의 WCF 서비스에서 서비스를 호출하는데 사용하는데 사용한 URL의 쿼리 스트링(query string)을 알아낼 수 없을까? 이 질문이 이번 포스트의 본질이 되겠다. 결론부터 말하면 "조낸 쉽게 알아낼 수 있다" 이다.

HttpRequestMessageProperty Class

HTTP 트랜스포트(transport)를 사용하는 WCF 서비스는 HttpRequestMessageProperty 클래스를 통해 서비스를 호출하는데 사용된 HTTP 요청(request)에 대한 정보를 얻을 수 있다. HttpRequestMessageProperty 클래스를 통해 얻을 수 있는 정보는 HTTP 메서드(닷넷 메서드 같은 것이 아닌 HTTP GET, HTTP POST 등등을 말한다), 쿼리 스트링(!), HTTP 헤더가 되겠다.

문제는 HttpRequestMessageProperty 객체를 어떻게 얻어내는가가 되겠다. 서비스의 메서드 내에서는 항상 액세스가 가능한 OperationContext객체가 존재하며 이 객체는 OperationContext.Current 정적 속성을 통해 액세스가 가능하다. OperationContext 객체는 IncomingMessageProperties 컬렉션을 제공하는데 이 컬렉션 내에 HttpRequestMessageProperty 클래스의 인스턴스가 존재하는 것이다. 이렇게 IncomingMessageProperties 컬렉션이 HttpRequestMessageProperty 객체를 꼽아주는 작업은 WCF 바인딩을 구성하는 Http 트랜스포트가 되겠다. 다시 말해 서비스 호출에 대한 HTTP 메시지가 도착하면 HTTP 트랜스포트는 HTTP 메시지로부터 필요한 정보를 추출하여 HttpRequestMessageProperty 객채를 생성하고 이 객체를 OperationContext의 IncomingMessageProperties 컬렉션에 추가 한다는 말이 되겠다.

WCF 바인딩, HTTP 트랜스포트나 OperationContext 클래스에 대한 상세한 내용은 MSDN 이나 필자의 저서인 "WCF 바이블" 5장 및 8장을 참고하기 바란다. (흠… 이런 식의 책 광고도 나쁘지 안쿠나!)

IncomingMessageProperties 컬렉션은 이름/값 형태의 컬렉션이기 때문에 HttpRequestMessageProperty 객체를 얻어오기 위해선 적당한 이름을 제공해야만 한다. 물론 무식하게 이 컬렉션을 모두 뒤져서 타입이 HttpRequestMessageProperty 인 아이템을 추출해 낼 수도 있겠지만 가급적 이런 무식한 코딩 방법은 피하자. 보다 우아한 코드를 작성하기 위해 HttpRequestMessageProperty 클래스가 제공하는 정석 속성인 Name 속성을 사용하면 된다. HttpRequestMessageProperty.Name 속성은 IncomingMessageProperties 컬렉션에서 HttpRequestMessageProperty 객체를 찾는데 사용할 수 있다.

이 정도 되면 독자들은 코드 서비스를 기대할 것 같다. (블로그 개편했으니 손님들 좀 모셔야… )

   1: [ServiceContract]
   2: interface ITestService
   3: {
   4:     [OperationContract]
   5:     string TestMethod();
   6: }
   7:  
   8: [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
   9: class TestService : ITestService
  10: {
  11:     public string TestMethod()
  12:     {
  13:         OperationContext ctx = OperationContext.Current;
  14:         var prop = ctx.IncomingMessageProperties[HttpRequestMessageProperty.Name]
  15:             as HttpRequestMessageProperty;
  16:         Console.WriteLine("Method: {0}", prop.Method);
  17:         Console.WriteLine("QueryString: {0}", prop.QueryString);
  18:         Console.WriteLine("Headers:");
  19:         foreach(string name in prop.Headers)
  20:         {
  21:             Console.WriteLine("    {0}:{1}", name, prop.Headers[name]);
  22:         }
  23:         return prop.QueryString;
  24:     }
  25: }

위 코드는 조낸 간단한 WCF 서비스 예제를 보여 준다. 위 코드에서 핵심은 회색으로 강조해 놓은 코드로서 앞서 열심히 말로 설명했던 내용이다. 다시 말해, OperationContext.Current 속성에서 OperationContext 객체를 얻고 이 객체가 제공하는 IncomingMessageProperties 컬렉션에서 HttpRequestMessageProperty 객체를 얻어내는 것이다.

일단 HttpRequestMessageProperty 객체를 얻어 낸 후에는 이 객체가 제공하는 Method 속성, QueryString 속성, 그리고 Headers 컬렉션을 사용하여 원하는 정보를 추출하면 된다.

당췌 WCF 서비스에서 URL의 쿼리 스트링을 어디다 쓸까?

HTTP 트랜스포트를 사용하는 WCF 서비스라면 쿼리 스트링은 여러모로 유용하게 사용할 수 있다. 대표적으로 쿼리 스트링을 빡세게 사용하는 예는 WCF이다. 닷넷 프레임워크 3.5에 추가된 WCF의 REST 지원 기능은 SOAP(Simple Object Access Protocol)이 아닌 HTTP GET/POST를 통해 서비스를 호출하도록 해준다. 특히 HTTP GET을 사용하는 서비스 호출은 서비스 호출에 필요한 매개변수를 쿼리 스트링을 통해 전달한다. 두말할 것도 없이 WCF는 HttpRequestMessageProperty 클래스의 속성들을 통해 매개변수를 추출한다. 이외에도 HTTP 헤더 컬렉션에 기록된 Accept 헤더 값을 사용하여 클라이언트에게 XML을 반환할 것인지 JSON을 반환할 것인지를 결정하기도 한다.

이 외에도 WCF 서비스 측에서 HTTP URL의 쿼리 스트링을 활용할 수 있는 분야는 매우 많다. 다만 포스트가 길어지는 귀차니즘을 어쩔 수 없어서 독자들의 상상력에 맡기고자 한다.

주의 사항

WCF 서비스는 다양한 바인딩을 사용할 수 있다. BasicHttpBinding 부터 시작해서 WSHttpBinding, NetTcpBinding, NetNamedPipeBinding 등등 매우 다양한 상황에서 다양하게 적용이 가능한 바인딩들이다. 이 말은 WCF 서비스가 다양한 바인딩 중 하나를 사용하거나 2개 이상의 바인딩을 사용할 수도 있다는 것이다. 이 말을 잘근잘근 곱씹어 보면 WCF 서비스에서 바인딩에 관련된 정보에 의지하거나 특정 바인딩을 가정할 수 없다는 말도 된다. 다시 말해 WCF 서비스 코드 내에서 서비스 호출 시에 사용된 HTTP 헤더의 값을 읽으려고 시도하는 코드는 WCF 서비스가 HTTP 관련 바인딩들(BasicHttpBinding, WSHttpBinding 등)을 통해서 호출될 때에만 유효한 코드가 된다는 말이 되겠다.


경고 : 이 글을 무단으로 복제/스크랩하여 타 게시판, 블로그에 게시하는 것은 허용하지 않습니다.