WCF REST 스타터 키트에 대한 개발자 가이드
Aaron Skonnard, Pluralsight
2009년 8월
참고: 이 문서는 WCF REST Starter Kit Preview 2 릴리스를 기반으로 합니다.
개요
WCF(Windows Communication Foundation) 3.5에서는 .NET에서 RESTful 서비스를 빌드하기 위한 "웹" 프로그래밍 모델을 도입했습니다. WCF 3.5는 다양한 RESTful 서비스를 빌드하기 위한 견고한 기반을 마련하지만, 개발자는 빌드하는 각 RESTful 서비스에 대해 많은 양의 보일러 플레이트 코드를 구현하고 중요한 HTTP 프로토콜 기능을 직접 처리해야 합니다. WCF REST 시작 키트는 REST 개발을 더욱 간소화하는 것을 목표로 하는 WCF 확장 및 프로젝트 템플릿 세트를 제공합니다. WCF REST 스타터 키트는 현재 "미리 보기" 기술로 간주되지만 많은 기능이 향후 버전의 .NET Framework로 전환될 가능성이 높습니다.
이 백서에서는 다양한 WCF REST 스타터 키트 기능을 자세히 살펴보고 현재 가장 일반적인 REST 개발 시나리오 중 일부를 처리하는 데 사용할 수 있는 방법을 보여 줍니다. Microsoft는 Microsoft .NET의 일부로 RESTful 서비스에 대한 풍부한 플랫폼을 제공하기 위해 최선을 다하고 있습니다.
REST 개념 또는 WCF 3.5 REST 프로그래밍 모델에 익숙하지 않은 경우 계속하기 전에 WCF 3.5를 사용하여 RESTful Services를 디자인하고 빌드하는 가이드를 읽어보세요.
WCF REST 스타터 키트 소개
WCF 3.5 출시 이후 Microsoft는 .NET 플랫폼에서 RESTful 서비스를 더 쉽게 빌드하고 사용하는 프로세스를 만들기 위해 노력하고 있습니다. 이러한 노력의 결과 중 하나는 WCF REST Starter Kit라고 하는 항목에 패키지된 새로운 도우미 클래스, 확장 메서드 및 Visual Studio 프로젝트 템플릿 모음입니다. 현재 CodePlex에서 WCF REST 스타터 키트를 다운로드할 수 있지만 많은 기능이 공식 .NET Framework의 이후 버전으로 전환될 수 있습니다. MSDN WCF REST 방문 페이지에서 WCF REST 시작 키트에 대한 최신 정보를 찾을 수 있습니다.
WCF REST 시작 키트(미리 보기 2)에는 일반적인 REST 프로그래밍 작업을 간소화하기 위해 코드에서 활용할 수 있는 세 가지 새로운 .NET 어셈블리가 함께 제공됩니다(그림 1 참조).
어셈블리 이름 | 묘사 |
---|---|
Microsoft.ServiceModel.Web |
WCF를 사용하여 RESTful 서비스를 빌드하고 호스팅하는 프로세스를 간소화하는 새로운 클래스 및 확장 메서드 집합을 포함합니다. |
Microsoft.Http |
HTTP를 사용하여 RESTful 서비스를 사용하는 프로세스를 간소화하는 새 클래스 및 확장 메서드 집합을 포함합니다. |
Microsoft.Http.Extensions |
특정 유형의 RESTful 서비스 및 응답 형식을 사용하는 새 클래스 및 확장 메서드 집합을 포함합니다. |
그림 1: WCF REST 시작 키트 어셈블리
Microsoft.ServiceModel.Web 어셈블리에는 RESTful 서비스를 호스팅하기 위해 특별히 설계된 새 WebServiceHost2 클래스(WCF 3.5의 WebServiceHost에서 파생됨)가 포함되어 있습니다. 이 클래스는 몇 가지 REST 관련 기능을 부트스트랩하고, RESTful 서비스를 더 쉽게 빌드하고 다른 사용자가 더 쉽게 사용할 수 있도록 기본 WCF 런타임을 구성합니다. 이 새 어셈블리에는 코드에서도 활용할 수 있는 몇 가지 .NET 특성 및 확장 메서드도 함께 제공됩니다. 이러한 확장을 사용하면 WebServiceHost2에서 제공하는 다양한 기능을 활용할 수 있습니다.
Microsoft.Http 어셈블리에는 RESTful 서비스를 사용하는 새 클라이언트 쪽 HTTP API가 포함되어 있습니다. 이 어셈블리에서 관심 있는 기본 클래스는 HttpClient입니다. HttpClient를 사용하면 HTTP GET, POST, PUT 및 DELETE 요청을 쉽게 발급하고 다양한 콘텐츠별 API를 통해 응답을 처리할 수 있습니다. 또한 양식 데이터 및 쿼리 문자열 값을 더 쉽게 보낼 수 있으며 형식화된 새 헤더 클래스 집합을 통해 HTTP 헤더 작업 프로세스를 간소화합니다. 이 새로운 클라이언트 쪽 API는 웹에 있는 모든 RESTful 서비스를 사용할 수 있는 보다 자연스러운 HTTP 환경을 제공합니다.
마지막으로 Microsoft.Http.Extensions 어셈블리에는 특정 시나리오에 초점을 맞춘 몇 가지 특수한 HttpClient 파생 클래스가 포함되어 있습니다. 또한 다양한 형식(XML, JSON, Atom 피드 등)으로 HTTP 메시지 본문을 처리하는 데 중점을 두는 확장 메서드를 꽤 많이 제공합니다. 서비스를 사용할 때 HttpClient와 함께 이러한 확장 메서드를 활용합니다.
WCF REST 시작 키트에는 일반적인 REST 시나리오를 대상으로 하는 유용한 Visual Studio 프로젝트 템플릿 세트도 함께 제공됩니다(그림 2 참조). 이러한 프로젝트 템플릿은 위에서 설명한 어셈블리에 있는 새 클래스/확장을 활용하여 시작하는 데 필요한 상용구 코드를 제공합니다. 예를 들어 "싱글톤" 서비스(단일 리소스 노출)를 생성하기 위한 템플릿과 "컬렉션" 서비스를 생성하기 위한 템플릿(리소스 컬렉션 노출)이 있습니다. Atom 피드를 생성하기 위한 또 다른 템플릿과 완벽하게 작동하는 AtomPub 서비스를 생성하는 다른 템플릿이 있습니다. 이러한 템플릿은 이러한 다양한 REST 시나리오에 대한 서비스 구현을 바로 시작하는 데 도움이 될 수 있습니다.
프로젝트 템플릿 | 묘사 |
---|---|
REST Singleton 서비스 |
XML 및 JSON 표현을 모두 지원하는 단일 항목(GET, POST, PUT 및 DELETE)과 상호 작용하기 위한 샘플 싱글톤 리소스(SampleItem) 및 전체 HTTP 인터페이스를 정의하는 서비스를 생성합니다. |
REST 컬렉션 서비스 |
REST Singleton 서비스와 마찬가지로 SampleItem 리소스 컬렉션 관리도 지원합니다. |
Atom 피드 서비스 |
더미 데이터를 사용하여 샘플 Atom 피드를 노출하는 서비스를 생성합니다. |
AtomPub 서비스 |
미디어 항목뿐만 아니라 리소스 컬렉션을 관리할 수 있는 완벽하게 작동하는 AtomPub 서비스를 생성합니다. |
HTTP 일반 XML 서비스 |
RESTful 디자인 원칙을 완전히 준수하지 않고 대신 GET 및 POST 작업에만 의존하는 일반 오래된 XML(POX) 서비스에 대해 빌드할 수 있는 간단한 GET 및 POST 메서드를 사용하여 서비스를 생성합니다. |
그림 2: WCF REST 시작 키트 프로젝트 템플릿
다음 섹션에서는 이러한 어셈블리 및 프로젝트 템플릿에 대해 자세히 알아보고 그 과정에서 몇 가지 일반적인 REST 시나리오를 처리하는 방법을 살펴보겠습니다.
Microsoft.ServiceModel.Web을 사용하여 서비스 빌드 및 호스팅
WCF 서비스 프로젝트에서 WCF REST Starter Kit를 활용하려면 Microsoft.ServiceModel.Web에 있는 WebServiceHost2 클래스를 사용하도록 호스트 애플리케이션을 수정해야 합니다. 이 클래스는 호스트 애플리케이션에서 노출하는 모든 서비스 엔드포인트에 대한 새로운 WCF REST Starter Kit 기능을 부트스트랩합니다. 이 작업을 완료한 후에는 자동 도움말 페이지, HTTP 캐싱 지원, 새 예외 처리 동작 및 새 요청 가로채기 기능을 활용할 수 있습니다. 먼저 WebServiceHost2를 연결하는 방법을 보여 드리겠습니다. 그런 다음 이러한 새로운 기능 영역을 각각 살펴보겠습니다.
WebServiceHost2를 사용하여 REST 서비스 호스팅
WebServiceHost2 클래스는 WCF 3.5에 있는 WebServiceHost 클래스에서 파생됩니다. 따라서 다른 ServiceHost 파생 클래스와 마찬가지로 사용할 수 있습니다. 호스트 애플리케이션에서 자체 호스팅 기술을 사용하는 경우 지금 다음과 같은 코드가 있을 수 있습니다.
WebServiceHost host = new WebServiceHost(typeof(BookmarkService));
host.Open();
서비스와 함께 WCF REST Starter Kit 기능을 사용하기 시작하려면 코드에서 클래스 이름을 "WebServiceHost"에서 "WebServiceHost2"로 변경하기만 하면 됩니다.
WebServiceHost2 host = new WebServiceHost2(typeof(BookmarkService));
host.Open();
IIS에서 서비스를 호스팅할 때 WebServiceHost2를 활용할 수도 있습니다. 현재 IIS 내에서 WCF 서비스를 호스팅하는 경우 다음과 같은 SVC 파일이 있습니다.
<%@ ServiceHost Language="C#" Debug="true" Service="BookmarkService"
Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>
WCF REST 스타터 키트에는 새 WebServiceHost2Factory 클래스가 함께 제공됩니다. WebServiceHost2 인스턴스를 활성화하는 역할을 담당합니다. 팩터리 클래스를 WebServiceHost2Factory로 바꾸면 IIS 호스팅 서비스가 WebServiceHost2 인스턴스에서 자동으로 관리됩니다.
<%@ ServiceHost Language="C#" Debug="true" Service="BookmarkService"
Factory="Microsoft.ServiceModel.Web.WebServiceHost2Factory"%>
그렇다면 WebServiceHost2는 WCF 3.5와 함께 제공되는 WebServiceHost 클래스와 정확히 어떻게 다른가요? 두 가지 주요 작업을 수행합니다. 먼저 모든 엔드포인트의 WebHttpBehavior를 WebHttpBehavior2 인스턴스로 바꿉니다. 새 WebHttpBehavior2 클래스는 자동 도움말 페이지 기능 및 서버 "웹" 오류 처리 논리를 제공합니다. 둘째, 각 엔드포인트에 새 바인딩 요소를 추가하여 새 요청 가로채기 논리를 삽입합니다. 따라서 호스트 유형을 변경하기만 하면 WCF REST 서비스가 이러한 새로운 기능을 활용할 수 있습니다.
자동 도움말 페이지
WebServiceHost2를 사용하면 서비스가 자동으로 새로운 자동 도움말 페이지 기능의 이점을 누릴 수 있으며 이는 RESTful 서비스에 큰 도움이 됩니다. 끝에 "도움말"이 추가된 서비스의 기본 주소로 이동하여 도움말 페이지를 볼 수 있습니다(그림 3 참조).
도움말 페이지에서는 [WebGet] 또는 [WebInvoke]로 주석이 추가된 각 WCF 서비스 작업에 대해 사람이 읽을 수 있는 설명을 제공하며, 각 작업에 대해 URI 템플릿, 지원되는 HTTP 작업 및 요청/응답 형식에 대해 설명합니다. 기본적으로 소비자가 알아야 할 모든 항목입니다.
각 요청/응답에 대해 도움말 페이지에서는 소비자가 서비스와 통합하는 데 사용할 수 있는 XML 스키마 및 해당 샘플 XML 인스턴스도 제공합니다. 소비자는 스키마를 사용하여 적절한 클라이언트 쪽 직렬화 가능 형식을 생성하거나 샘플 XML 문서를 검사하여 적절한 XML 처리 코드를 작성하는 방법을 수동으로 결정할 수 있습니다. 두 방법 모두 유용합니다.
그림 3: RESTFul 서비스에 대한 자동 도움말 페이지
도움말 페이지는 Atom 피드로 반환된다는 점에 유의해야 합니다. 대부분의 웹 브라우저는 사람이 쉽게 볼 수 있도록 기본 제공 피드 렌더링을 제공하며, 이는 Internet Explorer가 그림에서 수행하는 작업입니다. 그러나 피드이므로 소비자는 원하는 경우 설명을 프로그래밍 방식으로 사용할 수도 있습니다. Internet Explorer 옵션에서 "피드 읽기 보기"를 해제하는 경우 실제로 피드 XML이 렌더링되는 것을 볼 수 있습니다. 페이지의 원본을 확인하여 피드 XML을 검사할 수도 있습니다.
기본적으로 도움말 페이지는 끝에 "도움말"이 추가된 기본 주소에서 사용할 수 있지만 WebServiceHost2의 HelpPageLink 속성을 통해 도움말 페이지 주소를 사용자 지정할 수 있습니다.
다음 예제와 같이 Microsoft.ServiceModel.Web에 있는 새 [WebHelp] 특성을 통해 각 RESTful 작업에 사람이 읽을 수 있는 설명을 추가할 수도 있습니다.
[WebHelp(Comment = "Returns the user account details for the authenticated user.")]
[WebGet(UriTemplate = BookmarkServiceUris.User)]
[OperationContract]
User GetUserAsXml(string username)
{
return HandleGetUser(username);
}
이제 호스트 애플리케이션을 다시 실행하고 도움말 페이지로 돌아가면 GetUserAsXml 설명 내에 이 메모 텍스트가 표시됩니다(그림 4 참조).
그림 4: 사용자 지정 설명이 있는 자동 도움말 페이지
이 새로운 도움말 페이지를 사용하면 RESTful 서비스를 자동으로 검색할 수 있으므로 궁극적으로 다른 사용자가 더 쉽게 사용할 수 있습니다. 소비자는 서비스의 URI 디자인, 지원되는 HTTP 작업 및 요청/응답 형식을 검색할 수 있으며, 설명은 ASP.NET Web Services에서 작동하는 방식과 유사하게 항상 WCF 코드와 동기화 상태를 유지합니다.
여전히 전체 클라이언트 쪽 코드 생성 환경(la WSDL)을 얻지 못하지만 이 도움말 페이지를 새 HttpClient 기능과 결합하면 실제로 필요하지 않습니다.
ExceptionHandling
RESTful 서비스 구현의 더 지루한 측면 중 하나는 특히 WCF 서비스 작업의 컨텍스트 내에서 적절한 HTTP 상태 코드 및 설명을 반환하는 것과 같은 일부 HTTP 프로토콜 세부 정보를 직접 처리하는 것입니다. 다음 코드는 권한이 없는 사용자를 확인하는 방법을 보여 줍니다. 필요한 경우 401 "권한 없음" 응답을 반환합니다.
if (!IsUserAuthorized(username)) {
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "Unauthorized";
return null;
}
이 코드는 매우 어렵지는 않지만 자세한 응답 메시지를 반환하지 않습니다. 이는 일반적으로 소비자에게 매우 유용합니다. 이러한 유형의 자세한 응답 메시지를 반환하려는 경우 WCF 작업 내에서 쉽게 수행할 수 있는 방법이 없기 때문에 복잡성이 크게 커집니다.
이 일반적인 시나리오를 간소화하기 위해 WCF REST Starter Kit는 호출자에게 HTTP 오류를 매우 쉽게 반환할 수 있는 새 WebProtocolException 클래스를 제공합니다. WCF 서비스 작업 내에서 WebProtocolException 인스턴스를 throw하여 HTTP 상태 코드 및 오류 메시지를 지정하고(WebServiceHost2를 사용 중이라고 가정) 기본 런타임이 적절한 HTTP 응답 메시지를 생성합니다.
다음 예제에서는 다른 HTTP 상태 코드 및 오류 메시지를 지정하는 몇 가지 다른 WebProtocolException 인스턴스를 throw하는 방법을 보여 줍니다.
if (!IsUserAuthorized(username)) {
throw new WebProtocolException(HttpStatusCode.Unauthorized,
"Missing or invalid user key (supply via the Authorization header)", null);
}
if (bookmark_id <= 0)
throw new WebProtocolException(HttpStatusCode.BadRequest,
"The bookmark_id field must be greater than zero", null);
WebProtocolException이 throw되면 사용자 지정 WCF 오류 처리기(WebHttpBehavior2에서 도입됨)에 의해 처리됩니다. 오류 처리기는 WebProtocolException 인스턴스를 소비자의 혜택에 대한 자세한 응답을 포함하는 적절한 HTTP 응답 메시지로 변환합니다.
그림 5는 브라우저에서 렌더링될 때 첫 번째 WebProtocolException의 모양을 보여 줍니다. 결과 XHTML이 "세부 정보" 메시지와 함께 HTTP 상태 코드를 명확하게 표시하는 방법을 확인합니다. 이 표준 XHTML 템플릿은 WebProtocolException 클래스에 기본 제공되므로 작동 방식을 좋아한다면 더 이상 아무것도 할 필요가 없으며 소비자가 합리적인 것을 받게 됩니다.
그림 5: 브라우저 렌더링된 WebProtocolException
그러나 결과 XHTML을 사용자 지정하려는 경우 다른 생성자 오버로드 중 하나를 사용하고 여기에 설명된 대로 반환하려는 정확한 XHTML을 제공할 수 있습니다.
if (!IsUserAuthorized(username)) {
throw new WebProtocolException(HttpStatusCode.Unauthorized, "Unauthorized",
new XElement("html",
new XElement("body"),
new XElement("h1", "Unauthorized"),
new XElement("p", "Missing or invalid user key " +
"(supply via the Authorization header)")), true, null);
}
그림 6은 브라우저에서 렌더링될 때 이 WebProtocolException을 throw한 결과를 보여 줍니다.
그림 6: 사용자 지정 XHTML 응답 있는 WebProtocolException
이 방법을 사용하면 응답 XHTML에 대한 완전한 자유를 얻을 수 있습니다.
그러나 XHTML에서 사람이 읽을 수 있는 오류 메시지를 반환하는 데 관심이 없는 경우 대신 XML로 serialize되는 사용자 지정 형식을 반환할 수 있습니다(DataContractSerializer 사용). 다음 코드 예제에서는 CustomErrorMessage라는 사용자 지정 형식을 사용하여 이 작업을 수행하는 방법을 보여 줍니다.
if (!IsUserAuthorized(username)) {
throw new WebProtocolException(HttpStatusCode.Unauthorized,
"Custom error message",
new CustomErrorMessage() {
ApplicationErrorCode = 5000,
Description = "Authentication token missing" },
null);
}
이 경우 소비자는 그림 7에 설명된 사용자 지정 XML 메시지를 받습니다.
그림 7: 사용자 지정 XML 응답 있는 WebProtocolException
또한 WCF 작업이 응답에 WebMessageFormat.Json을 지정하는 경우 결과 XML은 소비자에게 JSON을 반환하기 위해 DataContractJsonSerializer를 사용하여 serialize됩니다.
ASP.NET 사용자 지정 오류 기능과 통합하여 한 단계 더 나아갈 수도 있습니다. Global.asax에 일부 코드를 추가하여 WebProtocolException이 있는지 확인하고 소비자를 찾을 때 사용자 지정 오류 페이지로 리디렉션하여 이 작업을 수행할 수 있습니다.
protected void Application_EndRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Error != null)
{
WebProtocolException webEx =
HttpContext.Current.Error as WebProtocolException;
if (webEx != null && webEx.StatusCode == HttpStatusCode.BadRequest)
{
HttpContext.Current.ClearError();
HttpContext.Current.Response.Redirect("BadRequest.htm");
}
if (webEx != null && webEx.StatusCode == HttpStatusCode.Unauthorized)
{
HttpContext.Current.ClearError();
HttpContext.Current.Response.Redirect("Unauthorized.htm");
}
}
}
WCF REST Starter Kit SDK에는 이러한 WebProtocolException 기능이 작동하는 방식을 보여 주는 두 가지 샘플이 있습니다. 하나는 "WebException"이고 다른 하나는 "WebException2"입니다. 결국 이러한 기능을 사용하면 .NET 개발자에게 보다 자연스러운 모델인 예외를 throw하여 설명이 포함된 응답 메시지를 포함하는 사용자 지정 HTTP 오류 메시지를 훨씬 쉽게 생성할 수 있습니다.
캐싱 지원
REST의 주요 잠재적 이점 중 하나는 HTTP 캐싱입니다. 그러나 이러한 이점을 실현하려면 요청 및 응답 메시지에서 다양한 HTTP 캐싱 헤더를 활용해야 합니다. WebOperationContext 인스턴스를 통해 요청/응답 헤더에 수동으로 액세스하여 WCF 서비스 작업 내에서 이 작업을 수행할 수 있지만 제대로 수행하는 것은 간단하지 않습니다.
또한 WCF REST 스타터 키트는 다양한 GET 작업에 선언적으로 적용할 수 있는 [WebCache] 특성을 통해 캐싱을 제어하기 위한 더 간단한 모델을 제공합니다. 이 특성을 사용하면 각 작업에 대해 캐싱 프로필을 지정할 수 있으며 캐싱 동작(CachingParameterInspector)은 모든 기본 HTTP 캐싱 세부 정보를 처리합니다.
[WebCache] 구현은 ASP.NET 출력 캐싱을 기반으로 하며 System.Web.UI.OutputCacheParameters 클래스에 있는 대부분의 동일한 속성을 제공합니다. WCF 런타임과 동일한 동작을 통합하기만 하면 WCF 서비스 작업에 해당 동작을 선언적으로 쉽게 적용할 수 있습니다. 다음 예제에서는 60초 동안 [WebGet] 응답을 캐시하는 방법을 보여줍니다.
[WebCache(Duration = 60)]
WebGet(UriTemplate = BookmarkServiceUris.PublicBookmarks)]
OperationContract]
ookmarks GetPublicBookmarksByTagAsXml(string tag)
return HandleGetPublicBookmarks(tag);
그러나 이 작업은 제공된 각 "태그"에 대해 다른 응답을 반환할 수 있으므로 태그별로 캐시된 응답을 변경하려고 합니다. 여기에 설명된 대로 VaryByParam 속성(ASP.NET 잘 알고 있을 수 있음)을 사용하여 이 작업을 수행할 수 있습니다.
[WebCache(Duration = 60, VaryByParam = "tag")]
[WebGet(UriTemplate = BookmarkServiceUris.PublicBookmarks)]
[OperationContract]
Bookmarks GetPublicBookmarksByTagAsXml(string tag)
{
return HandleGetPublicBookmarks(tag);
}
VaryByParam 외에도 [WebCache] 특성은 출력 캐시 항목에 영향을 주는 다양한 변수를 지정할 수 있는 VaryByHeader 및 VaryByCustom을 제공합니다. 또한 Location을 사용하여 응답을 캐시할 수 있는 위치(예: Any, Client, Server, ServerAndClient 등)를 제어하고 캐싱을 완전히 방지하려면 NoStore 속성을 "true"로 설정할 수 있습니다.
[WebCache]는 web.config있는 "출력 캐시 프로필"을 참조할 수 있는 CacheProfile 속성도 지원합니다. 예를 들어 다음 web.config 응답이 60초 동안 캐시되고 캐시된 응답이 어디에나 저장될 수 있으며 캐시 항목이 "tag" 매개 변수에 따라 달라지게 되도록 지정하는 "CacheFor1Min"이라는 출력 캐시 프로필을 포함합니다.
<configuration>
<system.web>
<compilation debug="true"/>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<clear/>
<add name="CacheFor1Min" duration="60" enabled="true"
location="Any" varyByParam="tag"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
이렇게 하면 컴파일된 WCF 코드에서 캐싱 동작을 분리할 수 있습니다. 다음과 같이 [WebCache] 특성을 통해 WCF 작업에 이 출력 캐시 프로필을 적용할 수 있습니다.
[WebCache(CacheProfileName="CacheFor1Min")]
WebGet(UriTemplate = BookmarkServiceUris.PublicBookmarks)]
OperationContract]
ookmarks GetPublicBookmarksByTagAsXml(string tag)
return HandleGetPublicBookmarks(tag);
마지막으로 [WebCache] 특성을 사용하면 SqlDependency 속성을 통해 캐싱 동작을 SQL 종속성에 연결할 수도 있습니다. 이를 활용하려면 해당 데이터베이스에 대한 <sqlCacheDependency> 항목을 추가하여 web.config합니다. 그런 다음 [WebCache] 특성의 SqlDependency 속성을 사용하여 캐시 항목이 종속되어야 하는 데이터베이스 & 테이블 이름 쌍 목록을 지정합니다. 지정된 테이블이 수정되면 캐시 항목이 만료됩니다.
다음 web.config 새 <sqlCacheDependency> 항목을 구성하는 방법을 보여 줍니다.
<configuration>
<connectionStrings>
<add name="bmconn" connectionString=
"Data Source=.; Initial Catalog=BookmarksDB; Integrated Security=true" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true" pollTime="1000" >
<databases>
<add name="bmdb" connectionStringName="bmconn" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
그런 다음 작업에 [WebCache]를 적용하여 출력 캐싱 동작을 SQL 데이터베이스 내의 특정 테이블에 연결할 수 있습니다. 다음 예제에서는 출력 캐싱을 "책갈피" 테이블에 연결합니다.
[WebCache(SqlDependency="bmdb:Bookmarks", VaryByParam = "tag")]
WebGet(UriTemplate = BookmarkServiceUris.PublicBookmarks)]
OperationContract]
ookmarks GetPublicBookmarksByTagAsXml(string tag)
return HandleGetPublicBookmarks(tag);
이 경우 기본 책갈피 테이블의 데이터가 변경될 때까지 이 특정 작업의 출력이 캐시됩니다(각 고유한 "태그").
[WebCache] 특성을 사용하면 HTTP 캐싱 헤더를 직접 사용할 필요 없이 HTTP 캐싱을 훨씬 쉽게 활용할 수 있습니다. 기본 WCF 동작은 응답에 HTTP Cache-Control, Date, Expires 및 Vary HTTP 헤더를 삽입하는 작업을 처리합니다. 그러면 클라이언트는 응답을 캐시하고 향후 왕복 횟수를 줄일 수 있습니다.
WCF REST Starter Kit SDK에는 [WebCache] 기능을 더 자세히 사용하는 방법을 보여 주는 두 가지 완전한 샘플이 함께 제공됩니다. 하나는 "Caching1"이고 다른 하나는 "Caching2"라고 합니다. "Caching2" 샘플은 SQL 캐시 종속성을 사용하는 전체 예제를 제공합니다.
[WebCache] 기능 외에도 WCF REST 스타터 키트에는 조건부 GET 및 조건부 PUT 시나리오를 구현하는 프로세스를 간소화하는 ETags를 보다 쉽게 사용할 수 있는 몇 가지 확장 메서드가 포함되어 있습니다. 나중에 이 문서의 예를 살펴보겠습니다.
가로채기 요청
RESTful 서비스를 빌드할 때 또 다른 일반적인 요구는 "요청 가로채기"입니다. 예를 들어 모든 작업(예: 인증 또는 사용자 지정 디스패치 논리)에 적용할 서비스 동작을 구현해야 하는 경우 동작 모델을 통해 요청 처리 "인터셉터"를 런타임에 삽입할 수 있으므로 일반적으로 WCF 동작으로 구현하는 것이 가장 좋습니다. 유일한 문제는 WCF 동작 및 인터셉터를 작성하는 것은 매우 복잡하고 희미한 심장에 대한 것이 아닙니다. 따라서 이 일반적인 시나리오를 간소화하기 위해 WCF REST 스타터 키트는 보다 복잡한 WCF 확장성 구성 요소를 작성하지 못하도록 보호하는 훨씬 간단한 "요청 가로채기" 메커니즘을 제공합니다.
Microsoft.ServiceModel.Web 내에서 ProcessRequest라는 단일 추상 메서드를 정의하는 RequestInterceptor라는 새로운 추상 기본 클래스를 찾을 수 있습니다. 전체 클래스 정의는 다음과 같습니다.
public abstract class RequestInterceptor
{
protected RequestInterceptor(bool isSynchronous);
public bool IsSynchronous { get; }
public virtual IAsyncResult BeginProcessRequest(RequestContext context,
AsyncCallback callback, object state);
public virtual RequestContext EndProcessRequest(IAsyncResult result);
public abstract void ProcessRequest(ref RequestContext requestContext);
}
RequestInterceptor에서 클래스를 파생시키고 ProcessRequest(비동기 호출을 지원하려는 경우 BeginProcessRequest/EndProcessRequest)를 재정의합니다. ProcessRequest의 구현은 요청 가로채기 논리를 구현하는 위치입니다. 요청 메시지에 대한 액세스 권한을 제공하고 요청 파이프라인을 단락하고 응답 메시지를 반환하는 몇 가지 방법을 제공하는 요청 컨텍스트 인스턴스가 제공됩니다.
WebServiceHost2 클래스는 특정 서비스에 대해 구성된 RequestInterceptor 인스턴스의 컬렉션을 관리합니다. 호스트 인스턴스에서 Open을 호출하기 전에 Interceptors 컬렉션에 RequestInterceptor 인스턴스를 추가하기만 하면 됩니다. 그런 다음 Open을 호출하면 새 바인딩 요소를 통해 백그라운드에서 요청 처리 파이프라인에 삽입됩니다.
다음 예제에서는 API 키 인증을 수행하고 권한 없는 요청을 거부하는 RequestInterceptor를 구현하는 방법을 보여 줍니다.
public class AuthenticationInterceptor : RequestInterceptor
{
public AuthenticationInterceptor() : base(false) { }
public override void ProcessRequest(ref RequestContext requestContext)
{
if (!IsValidApiKey(requestContext))
GenerateErrorResponse(requestContext,
HttpStatusCode.Unauthorized,
"Missing or invalid user key (supply via the Authorization header)");
}
public bool IsValidUserKey(Message req, string key, string uri)
{
... // ommitted for brevity
}
public void GenerateErrorResponse(RequestContext requestContext,
HttpStatusCode statusCode, string errorMessage)
{
// The error message is padded so that IE shows the response by default
string errorHtml =
"<html><HEAD><TITLE>Request Error</TITLE></HEAD><BODY>" +
"<H1>Error processing request</H1><P>{0}</P></BODY></html>";
XElement response = XElement.Load(new StringReader(
string.Format(errorHtml, errorMessage)));
Message reply = Message.CreateMessage(MessageVersion.None, null, response);
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty()
{
StatusCode = statusCode
};
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
requestContext.Reply(reply);
// set the request context to null to terminate processing of this request
requestContext = null;
}
}
이제 WebServiceHost2 인스턴스를 처음 만들 때 인터셉터를 삽입하는 사용자 지정 ServiceHostFactory를 작성하여 IIS 호스팅 서비스에서 이 요청 인터셉터를 활용할 수 있습니다. 다음 예제에서는 이 작업을 수행하는 방법을 보여 줍니다.
public class SecureWebServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
WebServiceHost2 host = new WebServiceHost2(serviceType, true, baseAddresses);
host.Interceptors.Add(new AuthenticationInterceptor());
return host;
}
}
그런 다음 .svc 파일(팩터리 특성 사용)에서 SecureWebServiceHostFactory를 지정하면 인터셉터에서 자동으로 시작됩니다. 이는 이를 해제하는 데 필요한 동등한 WCF 동작, 인터셉터 및 특성 클래스를 작성하는 것보다 훨씬 쉽습니다.
WCF REST 스타터 키트 SDK에는 RequestInterceptor의 몇 가지 가능성을 보여 주는 몇 가지 추가 예제가 함께 제공됩니다. 샘플 중 하나는 RequestInterceptor를 사용하여 X-HTTPMethod-Override 동작을 구현하는 방법을 보여줍니다. RequestInterceptor 구현은 다음과 같습니다.
public class XHttpMethodOverrideInterceptor : RequestInterceptor
{
public XHttpMethodOverrideInterceptor() : base(true) {}
public override void ProcessRequest(ref RequestContext requestContext)
{
if (requestContext == null || requestContext.RequestMessage == null)
{
return;
}
Message message = requestContext.RequestMessage;
HttpRequestMessageProperty reqProp = (HttpRequestMessageProperty)
message.Properties[HttpRequestMessageProperty.Name];
string methodOverrideVal = reqProp.Headers["X-HTTP-Method-Override"];
if (!string.IsNullOrEmpty(methodOverrideVal))
{
reqProp.Method = methodOverrideVal;
}
}
}
X-HTTP-Method-Override 헤더를 찾은 경우 요청 메시지의 HTTP 메서드를 헤더에 있는 값으로 다시 설정합니다. 이렇게 하면 이 시나리오에 대한 매우 간단한 솔루션이 제공되고 모든 RESTful WCF 서비스 솔루션에서 쉽게 재사용할 수 있습니다.
WCF REST 시작 키트와 함께 제공하는 또 다른 RequestInterceptor 예제는 콘텐츠 형식 기반 디스패치입니다. 즉, HTTP Accept 또는 Content-Type 요청 헤더의 값에 따라 다른 서비스 작업으로 디스패치할 수 있도록 합니다. 다음 예제에서는 다른 RequestInterceptor 구현을 통해 이 작업을 수행하는 방법을 보여 줍니다.
public class ContentTypeRequestInterceptor : RequestInterceptor
{
public ContentTypeRequestInterceptor() : base(true) {}
public override void ProcessRequest(ref RequestContext requestContext)
{
if (requestContext == null) return;
Message request = requestContext.RequestMessage;
if (request == null) return;
HttpRequestMessageProperty prop = (HttpRequestMessageProperty)
request.Properties[HttpRequestMessageProperty.Name];
string format = null;
string accepts = prop.Headers[HttpRequestHeader.Accept];
if (accepts != null)
{
if (accepts.Contains("text/xml") || accepts.Contains("application/xml"))
{
format = "xml";
}
else if (accepts.Contains("application/json"))
{
format = "json";
}
}
else
{
string contentType = prop.Headers[HttpRequestHeader.ContentType];
if (contentType != null)
{
if (contentType.Contains("text/xml") ||
contentType.Contains("application/xml"))
{
format = "xml";
}
else if (contentType.Contains("application/json"))
{
format = "json";
}
}
}
if (format != null)
{
UriBuilder toBuilder = new UriBuilder(request.Headers.To);
if (string.IsNullOrEmpty(toBuilder.Query))
{
toBuilder.Query = "format=" + format;
}
else if (!toBuilder.Query.Contains("format="))
{
toBuilder.Query += "&format=" + format;
}
request.Headers.To = toBuilder.Uri;
}
}
}
다음은 RequestInterceptor 메커니즘을 사용하여 수행할 수 있는 작업의 몇 가지 예일 뿐입니다. 이 기술을 사용하여 로깅, 유효성 검사 또는 사용자 지정 캐싱과 같은 다양한 요청 처리 동작을 수행할 수 있습니다. 또한 솔루션은 RESTful 서비스에서 쉽게 재사용할 수 있습니다.
추가 클래스 및 확장 메서드
방금 설명한 주요 기능 외에도 WCF REST 스타터 키트에는 일반적인 REST 프로그래밍 작업을 간소화하는 다양한 확장 메서드가 포함되어 있습니다. 이러한 확장 메서드는 Microsoft.ServiceModel.Web 내의 여러 클래스에 분산되어 있습니다. 나는 여기에 그들 중 몇 가지를 강조 할 것이다.
WebOperationContextExtensions 클래스에는 핵심 WebOperationContext 클래스에 대한 확장 메서드 집합이 포함되어 있습니다. 그 중 일부는 URI 및 UriTemplate 조작을 더 쉽게 만들도록 설계되었습니다(GetBaseUri, GetRequestUri 및 BindTemplateToRequestUri). 그리고 나머지는 HTTP ETag 처리를 간소화하도록 설계되었습니다(여러 SetHashEtag 오버로드 및 ThrowIfEtagMissingOrStale을 통해). 다음은 WebOperationContextExtensions에 대한 클래스 정의를 보여 줍니다.
public static class WebOperationContextExtensions
{
public static Uri BindTemplateToRequestUri(this WebOperationContext context,
UriTemplate template, params string[] values);
public static Uri GetBaseUri(this IncomingWebRequestContext context);
public static NameValueCollection GetQueryParameters(
this IncomingWebRequestContext context);
public static Uri GetRequestUri(this IncomingWebRequestContext context);
public static string SetHashEtag<T>(this OutgoingWebResponseContext context,
T entityToHash);
public static string SetHashEtag<T>(this OutgoingWebResponseContext context,
BinaryFormatter formatter, T entityToHash);
public static string SetHashEtag<T>(this OutgoingWebResponseContext context,
XmlObjectSerializer serializer, T entityToHash);
public static string SetHashEtag<T>(this OutgoingWebResponseContext context,
XmlSerializer serializer, T entityToHash);
public static void ThrowIfEtagMissingOrStale(
this IncomingWebRequestContext context, string expectedEtag);
}
SerializationExtensions 클래스에는 XLinq를 사용하는 경우 XElement 인스턴스 간 개체 직렬화를 간소화하는 여러 확장 메서드가 포함되어 있습니다. 다음과 같은 여러 ToObject 및 ToXml 오버로드를 제공합니다.
public static class SerializationExtensions
{
public static TObject ToObject<TObject>(this XElement xml);
public static TObject ToObject<TObject>(this XElement xml,
XmlObjectSerializer serializer);
public static TObject ToObject<TObject>(this XElement xml,
XmlSerializer serializer);
public static XElement ToXml<TObject>(TObject obj);
public static XElement ToXml<TObject>(TObject obj,
XmlObjectSerializer serializer);
public static XElement ToXml<TObject>(TObject obj, XmlSerializer serializer);
}
마지막으로 SyndicationExtensions 클래스에는 SyndicationFeed 및 SyndicationItem 클래스를 통해 RSS/Atom 피드 작업을 간소화하는 몇 가지 확장 메서드가 포함되어 있습니다. 이러한 메서드를 사용하면 "자체" 링크, "편집" 링크 및 탐색 링크를 포함하여 피드에 다양한 유형의 링크를 더 쉽게 추가할 수 있습니다.
public static class SyndicationExtensions
{
public static void AddEditLink(this SyndicationItem entry, Uri uri);
public static void AddEditMediaLink(this SyndicationItem entry, Uri uri,
string contentType, long contentLength);
public static void AddNextPageLink(this SyndicationFeed feed, Uri uri);
public static void AddPreviousPageLink(this SyndicationFeed feed, Uri uri);
public static void AddSelfLink(this SyndicationFeed feed, Uri uri);
}
이러한 확장 메서드 외에도 Microsoft.ServiceModel.Web.SpecializedServices 네임스페이스에는 가장 일반적인 유형의 "특수화된" RESTful 서비스에 대한 서비스 계약 인터페이스 및 기본 클래스 정의 집합도 포함되어 있습니다(그림 8 참조). 여기서는 싱글톤 서비스, 컬렉션 서비스 및 AtomPub 서비스에 대한 형식을 찾을 수 있습니다.
인터페이스 | 기본 클래스 | 묘사 |
---|---|---|
ISingletonService<TItem> |
SingletonServiceBase<TItem> |
"singleton" REST 서비스(예: 단일 TItem 리소스만 노출하는 RESTful 서비스)에 대한 일반 서비스 계약 및 기본 구현을 정의합니다. |
ICollectionService<TItem> |
CollectionServiceBase<TItem> |
"컬렉션" REST 서비스(예: TItem 리소스 컬렉션을 노출하는 RESTful 서비스)에 대한 일반 서비스 계약 및 기본 구현을 정의합니다. |
IAtomPubService |
AtomPubServiceBase |
AtomPub 서비스에 대한 일반 서비스 계약 및 기본 구현을 정의합니다. |
그림 8: 특수 REST 서비스 계약 인터페이스 및 기본 클래스
이러한 형식은 각 서비스 유형에 대한 REST 계약 세부 정보를 정의하며, 리소스 정의(TItem) 및 핵심 CRUD 기능에 집중할 수 있도록 하면서 HTTP 세부 정보로부터 보호합니다.
이러한 표준 서비스 유형 중 하나를 구현하려는 경우 관심 있는 기본 클래스 및 해당 서비스 계약 정의에서 클래스를 파생하기만 하면 됩니다. 그런 다음 추상 메서드를 재정의하여 해당 리소스에 대한 CRUD 기능을 정의한 다음 호스트할 준비가 된 것입니다. HTTP 세부 정보는 기본 형식에서 처리되므로 걱정할 필요가 없습니다.
예를 들어 ICollectionService<TItem> 및 CollectionServiceBase<TItem> 형식을 사용하여 간단한 책갈피 서비스를 구현한다고 가정합니다. 리소스 종류에 책갈피를 지정하는 동안 두 형식에서 새 클래스를 파생시켜 이 작업을 수행할 수 있습니다. 그런 다음 OnAddItem, OnDeleteItem, OnGetItem, OnGetItems 및 OnUpdateItem을 포함하여 기본 클래스에 정의된 몇 가지 추상 메서드를 재정의합니다. 메서드 구현은 CRUD 기능을 정의합니다.
그림 9에서는 참조에 대한 전체 샘플 구현을 보여 줍니다.
public class BookmarkService : CollectionServiceBase<Bookmark>,
ICollectionService<Bookmark>
{
Dictionary<string, Bookmark> bookmarks = new Dictionary<string, Bookmark>();
protected override Bookmark OnAddItem(Bookmark initialValue, out string id)
{
id = Guid.NewGuid().ToString();
bookmarks.Add(id, initialValue);
return initialValue;
}
protected override bool OnDeleteItem(string id)
{
bookmarks.Remove(id);
return true;
}
protected override Bookmark OnGetItem(string id)
{
return bookmarks[id];
}
protected override IEnumerable<KeyValuePair<string, Bookmark>> OnGetItems()
{
return bookmarks;
}
protected override Bookmark OnUpdateItem(string id, Bookmark newValue)
{
bookmarks[id] = newValue;
return bookmarks[id];
}
}
그림 9: ICollectionService<TItem> 및 CollectionServiceBase<TItem> 사용하는 샘플 구현
이제 그림 9에 표시된 샘플 구현을 호스트할 준비가 완료되었습니다. 호스팅 측면을 보다 쉽게 만들기 위해 Microsoft.ServiceModel.Web.SpecializedServices 네임스페이스에는 각 특수 서비스 유형에 대한 특수 호스트 클래스도 포함되어 있습니다. 예를 들어 SingletonServiceHost, CollectionServiceHost 및 AtomPubServiceHost 클래스를 사용할 수 있습니다. 이러한 특수 호스트 유형은 백그라운드에서 필요한 WCF 동작을 구성하므로 걱정할 필요가 없습니다.
다음 코드 샘플에서는 간단한 콘솔 애플리케이션 내에서 그림 9에 표시된 BookmarkService 구현을 호스트하는 방법을 보여 줍니다.
class Program
{
static void Main(string[] args)
{
CollectionServiceHost host = new CollectionServiceHost(
typeof(BookmarkService),
new Uri("https://localhost:8080/bookmarkservice"));
host.Open();
Console.WriteLine("Host is up and running...");
Console.ReadLine();
host.Close();
}
}
이 콘솔 애플리케이션을 실행한 다음 서비스의 기본 주소로 이동하면 그림 10과 같이 책갈피 목록을 다시 가져와야 합니다(OnGetItems에서 반환).
그림 10: 실행 중인 BookmarkService로
기본 URI의 끝에 "/help"를 추가하면 REST 도움말 페이지가 표시됩니다(그리고 기본 URI 끝에 "/help"를 추가하면 REST 도움말 페이지가 표시됩니다(그림 11 참조).
그림 11: 책갈피 서비스 설명하는 도움말 페이지
도움말 페이지를 탐색하는 경우 이 서비스 구현은 각 논리 작업에 대해 아무 작업도 수행하지 않고 XML 및 JSON 메시지 형식을 모두 지원하는 것을 볼 수 있습니다.
이러한 동일한 기술을 사용하여 단일 서비스(단일 리소스만 노출하는 서비스) 또는 완벽하게 작동하는 AtomPub 서비스를 구현할 수 있습니다. 이 서비스는 오늘날 업계에서 매우 인기를 끌고 있습니다. 이러한 특수한 서비스 유형을 사용하면 제네릭 구현에 의해 부과된 제약 조건에 맞는 것으로 가정하여 RESTful 서비스를 훨씬 쉽게 시작하고 실행할 수 있습니다.
개발자가 이러한 특수 형식을 더 쉽게 사용할 수 있도록 하기 위해 WCF REST Starter Kit에는 이러한 "특수화된" 서비스 유형을 사용하여 새 서비스 구현을 만드는 프로세스를 부트스트랩하는 데 도움이 되는 Visual Studio 프로젝트 템플릿 세트도 함께 제공됩니다.
Visual Studio 프로젝트 템플릿
WCF REST Starter Kit에는 몇 가지 유형의 "특수화된" 서비스에 필요한 보일러 플레이트 코드를 제공하는 몇 가지 유용한 Visual Studio 프로젝트 템플릿이 함께 제공됩니다. WCF REST 시작 키트를 설치하면 Visual Studio 새 프로젝트 대화 상자에 새 프로젝트 템플릿 모음이 표시됩니다(그림 12 참조). 아래 그림 13에서는 각 프로젝트 템플릿이 제공하는 내용을 설명했습니다.
선택하기만 하면 나머지 프로젝트 세부 정보를 입력하고 확인을 누릅니다. 그런 다음 즉시 실행하고 빌드를 시작할 수 있는 기본 REST 프로젝트로 끝납니다. 프로젝트 템플릿은 기본적으로 이전 섹션에서 보여 준 것과 같은 서비스 구현을 빌드합니다.
이러한 프로젝트 템플릿 중 하나를 사용하는 경우 주요 초점은 리소스 클래스 정의를 수정하고 구현 전체에서 이러한 변경 내용을 전파하고(Visual Studio 리팩터링을 사용하여 이를 수행할 수 있음) 각 HTTP 작업에 대한 CRUD 메서드 스텁을 구현하는 것입니다.
WCF REST Starter Kit 프로젝트 템플릿을 사용하여 이러한 유형의 RESTful 서비스를 빌드하는 프로세스를 간소화하는 방법을 설명하는 몇 가지 예제를 살펴보겠습니다.
그림 12: WCF REST 시작 키트 프로젝트 템플릿
인터페이스 | 묘사 |
---|---|
Atom Feed WCF 서비스 |
SyndicationFeed를 프로그래밍 방식으로 생성하고 반환하는 방법을 보여 주는 샘플 WCF 서비스를 생성합니다. 비즈니스 데이터로 피드를 채우려면 구현을 변경하기만 하면 됩니다. |
AtomPub WCF 서비스 |
완전 규격 AtomPub 서비스에 대한 완전한 기본 구현을 생성하여 이 시나리오에 대해 작성해야 하는 코드의 양을 크게 줄이고 주로 비즈니스 리소스를 AtomPub 프로토콜에 매핑하는 방법에 집중할 수 있습니다. |
HTTP 일반 XML WCF 서비스 |
전체 HTTP 인터페이스를 지원하지 않는 XML over HTTP 서비스를 생성합니다. 대신 간단한 GET 및 POST 작업을 제공하고 실제로 RESTful이 아닌 XML 기반 서비스를 쉽게 빌드할 수 있습니다. |
REST 컬렉션 WCF 서비스 |
리소스 컬렉션 주위에 전체 HTTP 인터페이스(GET, POST, PUT 및 DELETE)를 노출하고 기본 리소스에 대한 XML 및 JSON 표현을 모두 제공하는 서비스를 생성합니다. |
REST Singleton WCF 서비스 |
단일 리소스 주위에 전체 HTTP 인터페이스(GET, POST, PUT 및 DELETE)를 노출하는 서비스를 생성하고 기본 리소스에 대한 XML 및 JSON 표현을 모두 제공합니다. |
그림 13: WCF REST 시작 키트 프로젝트 템플릿
REST Singleton Services
먼저 언제든지 현재 위치의 위치를 나타내는 단일 리소스를 노출하는 간단한 서비스를 만들어 보겠습니다. 새 "REST Singleton WCF 서비스" 프로젝트를 만들고 이름을 MyWhereabouts로 지정합니다. 프로젝트가 생성되면 서비스 구현이 포함된 service.svc 파일이 있습니다(코드는 실제로 service.svc.cs). 이 시점에서 실제로 F5 키를 눌러 초기 생성된 프로젝트가 완료되고 실행 준비가 되었음을 보여 주는 서비스를 테스트할 수 있습니다.
F5 키를 눌러 서비스를 로드하고 찾아보면 서비스는 브라우저에서 렌더링되는 XML 표현으로 <SampleItem> 리소스를 반환합니다(그림 14 참조).
그림 14: 생성된 싱글톤 서비스로 검색(변경 없음)
service.svc.cs 내의 소스 코드를 살펴보면 SampleItem에 대한 클래스 정의를 제공했음을 알 수 있습니다. 이 클래스는 서비스 구현에서 노출되는 단일 리소스를 나타냅니다. 노출하려는 실제 리소스를 나타내도록 이 클래스 정의를 수정하려고 합니다. 이 클래스는 파일 전체의 다른 위치에서 참조되므로 변경 내용을 전파하기 위해 Visual Studio의 리팩터링 지원을 활용할 수 있습니다. 초기 클래스의 모양은 다음과 같습니다.
// TODO: Modify the SampleItem. Use Visual Studio refactoring while modifying so
// that the references are updated.
/// <summary>
/// Sample type for the singleton resource.
/// By default all public properties are DataContract serializable
/// </summary>
public class SampleItem
{
public string Value { get; set; }
}
서비스 클래스 정의를 살펴보면 SingletonServiceBase<SampleItem> 및 ISingletonService<SampleItem>파생되는 것을 볼 수 있습니다. 이제 SampleItem 클래스 정의를 더 적절한 것으로 수정해야 합니다. "내 위치"를 노출하려고 하므로 클래스 이름을 MyWhereabouts로 변경합니다. 바로 리팩터링 메뉴를 사용하고 "'SampleItem' 이름을 'MyWhereabouts'로 바꿉니다."(그림 15 참조).
그림 15: Visual Studio 리팩터링을 사용하여 리소스 클래스 이름 변경
이 옵션을 선택하면 나머지 파일 전체에서 클래스 참조의 이름을 바꾸는 작업을 수행하므로 변경 후에도 모든 항목이 빌드되고 실행되어야 합니다.
이제 노출하려는 리소스 표현을 모델링하도록 클래스 정의를 수정하는 데만 집중할 수 있습니다. MyWhereabouts 리소스의 경우 Placename, Timezone, Lattitude 및 경도라는 몇 가지 공용 필드를 추가합니다. 수정된 리소스 클래스는 다음과 같습니다.
public class MyWhereabouts
{
public string Placename { get; set; }
public string Timezone { get; set; }
public string Lattitude { get; set; }
public string Longitude { get; set; }
}
이 변경 후 이 백서를 작성하는 동안 MyWhereabouts 필드 초기화 코드를 수정하여 이러한 필드를 현재 위치로 설정했습니다.
public class Service : SingletonServiceBase<MyWhereabouts>,
ISingletonService<MyWhereabouts>
{
// TODO: This variable used by the sample implementation. Remove if needed
MyWhereabouts item = new MyWhereabouts()
{
Placename = "Fruit Heights",
Timezone = "GMT-07:00 Mountain Time",
Lattitude = "41.016962",
Longitude = "-111.904238"
};
이러한 몇 가지 변경 내용을 적용하면 이제 F5 키를 다시 눌러 service.svc 파일로 이동할 수 있으며 브라우저에 표시된 <MyWhereabouts> 리소스가 표시됩니다(그림 16 참조).
그림 16: MyWhereabouts 리소스 찾아보기
이 일반 서비스 구현은 전체 HTTP 인터페이스(GET, POST, PUT 및 DELETE)를 지원하며 각 작업은 XML 및 JSON 표현을 모두 지원합니다. URI의 끝에 "?format=json"을 추가하기만 하면 JSON 형식으로 현재 리소스를 검색합니다. 이제 Fiddler를 사용하거나 사용자 지정 클라이언트를 작성하여 다른 작업(POST, PUT 및 DELETE)을 테스트할 수 있습니다.
REST 컬렉션 서비스
다음으로 다른 BookmarkService를 구현하는 REST 컬렉션 서비스를 만들어 보겠습니다. 먼저 "REST 컬렉션 WCF 서비스" 템플릿을 선택하여 새 프로젝트를 만듭니다. 이전 예제와 마찬가지로 CollectionServiceBase<SampleItem> 및 ICollectionService<책갈피>파생되는 서비스 클래스와 함께 SampleItem이라는 리소스 클래스가 포함된 WCF 프로젝트로 끝납니다. 이러한 기본 형식은 앞에서 설명한 대로 전체 HTTP 인터페이스를 구현합니다. 이제 몇 가지 변경만 하면됩니다.
가장 먼저 변경해야 하는 것은 리소스 클래스의 이름입니다. "SampleItem"에서 "Bookmark"로 변경하고 Visual Studio 리팩터링을 활용하여 프로젝트 전체에서 변경 사항을 다시 전파하겠습니다. 이제 책갈피 리소스를 나타내는 데 필요한 필드로 책갈피 클래스를 채울 수 있습니다. 책갈피 클래스 정의를 다음으로 변경합니다.
public class Bookmark
{
public Uri Url { get; set; }
public string User { get; set; }
public string Title { get; set; }
public string Tags { get; set; }
public bool Public { get; set; }
public DateTime LastModified { get; set; }
}
이러한 변경 내용이 적용되면 새 책갈피 컬렉션 서비스를 테스트할 준비가 된 것입니다. Visual Studio에서 F5 키를 누르면 서비스가 로드되고 service.svc 파일로 이동됩니다. 브라우저가 나타나면 Bookmark 컬렉션이 현재 비어 있으므로 빈 <ItemInfoList> 요소가 표시됩니다.
이때 Fiddler와 같은 항목을 사용하여 컬렉션에 일부 책갈피를 추가하거나(POST를 통해) 서비스 생성자에서 책갈피 항목 컬렉션을 미리 채울 수 있습니다. 책갈피 컬렉션을 채우면 서비스를 다시 탐색할 때 몇 가지 <책갈피> 요소가 다시 표시됩니다(그림 17 참조). 결과 목록에는 개별 책갈피 리소스에 대한 링크가 포함되어 있습니다.
<ItemInfo> 요소 중 하나에 있는 <EditLink> 요소 중 하나를 따라 개별 책갈피로 이동할 수 있습니다. 예를 들어 "https://localhost:26826/Service.svc/7b5b4a15-3b05-4f94-a7b8-1f324b5cfc7d"로 이동하여 컬렉션의 첫 번째 책갈피를 가져올 수 있습니다(그림 18 참조).
그림 17: 책갈피 컬렉션 서비스 찾아보기
그림 18: 컬렉션 내의 단일 책갈피로 검색
이 시점에서 추가 코딩 없이 POST, PUT 및 DELETE 작업을 사용할 수도 있습니다. 생성된 프로젝트 템플릿에는 각 템플릿에 대한 기본 구현이 이미 포함되어 있습니다. 새 <책갈피> 요소를 서비스의 루트 주소에 게시할 수 있으며 새 책갈피 리소스를 생성하고 새 ID를 할당하고 해당 위치 헤더를 사용하여 201 만든 응답을 반환합니다. 개별 책갈피 URI에 <책갈피> 요소를 배치하여 업데이트를 수행할 수도 있습니다. 또한 DELETE 요청을 개별 책갈피 리소스에 보내 컬렉션에서 제거할 수 있습니다.
볼 수 있듯이 이 특정 유형의 RESTful 서비스(컬렉션 지향 서비스)의 경우 WCF REST 스타터 키트를 통해 구현을 시작하고 거의 코딩하지 않고 실행할 수 있습니다.
Atom Feed Services
간단한 Atom 피드 서비스를 생성해야 하는 경우 WCF REST 시작 키트에서 "Atom Feed WCF 서비스" 형식의 새 프로젝트를 만듭니다. 그림 19와 같이 샘플 피드 구현을 사용하여 WCF 서비스를 생성합니다. 이 서비스는 as-is 실행하고 표준 Atom 판독기에서 렌더링할 수 있는 샘플 Atom 피드를 생성합니다(그림 20을 참조하여 IE에서 렌더링되는 방법을 확인하세요).
// TODO: Please set IncludeExceptionDetailInFaults to false in production
// environments
[ServiceBehavior(IncludeExceptionDetailInFaults = true),
AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed), ServiceContract]
public partial class FeedService
{
// TODO: Modify the URI template and method parameters according to your
// application. An example URL is http://<url-for-svc-file>?numItems=1
[WebHelp(Comment = "Sample description for GetFeed.")]
[WebGet(UriTemplate = "?numItems={i}")]
[OperationContract]
public Atom10FeedFormatter GetFeed(int i)
{
SyndicationFeed feed;
// TODO: Change the sample content feed creation logic here
if (i < 0) throw new WebProtocolException(HttpStatusCode.BadRequest,
"numItems cannot be negative", null);
if (i == 0) i = 1;
// Create the list of syndication items. These correspond to Atom entries
List<SyndicationItem> items = new List<SyndicationItem>();
for (int j = 1; j <= i; ++j)
{
items.Add(new SyndicationItem()
{
// Every entry must have a stable unique URI id
Id = String.Format(CultureInfo.InvariantCulture,
"http://tempuri.org/Id{0}", j),
Title = new TextSyndicationContent(
String.Format("Sample item '{0}'", j)),
// Every entry should include the last time it was updated
LastUpdatedTime = new DateTime(
2008, 7, 1, 0, 0, 0, DateTimeKind.Utc),
// The Atom spec requires an author for every entry. If the entry has
// no author, use the empty string
Authors =
{
new SyndicationPerson()
{
Name = "Sample Author"
}
},
// The content of an Atom entry can be text, xml, a link or arbitrary
// content. In this sample text content is used.
Content = new TextSyndicationContent("Sample content"),
});
}
// create the feed containing the syndication items.
feed = new SyndicationFeed()
{
// The feed must have a unique stable URI id
Id = "http://tempuri.org/FeedId",
Title = new TextSyndicationContent("Sample feed"),
Items = items
};
feed.AddSelfLink(
WebOperationContext.Current.IncomingRequest.GetRequestUri());
#region Sets response content-type for Atom feeds
WebOperationContext.Current.OutgoingResponse.ContentType = ContentTypes.Atom;
#endregion
return feed.GetAtom10Formatter();
}
}
그림 20: Internet Explorer 렌더링된 샘플 피드
이 템플릿은 일반적인 Atom 피드 서비스를 생성하기 위한 샘플 코드만 제공합니다. 비즈니스 엔터티를 SyndicationFeed 인스턴스에 매핑하도록 코드를 수정해야 하며, 각 개별 엔터티를 제공하는 필드를 사용하는 새 SyndicationItem 인스턴스에 매핑해야 합니다.
AtomPub 서비스
Atom 게시 프로토콜을 준수하는 서비스를 구현하려는 경우 WCF REST 시작 키트와 함께 제공되는 "Atom Publishing Protocol WCF Service" 프로젝트 템플릿을 사용해야 합니다. 이 템플릿은 단일 샘플 컬렉션을 노출하는 완전한 AtomPub 서비스를 생성합니다. 생성된 서비스 클래스는 앞에서 설명한 AtomPubServiceBase 및 IAtomPubService에서 파생됩니다.
service.svc 파일로 이동하여 서비스를 즉시 테스트할 수 있으며, 서비스는 지원하는 컬렉션을 설명하는 AtomPub 서비스 문서를 반환합니다(그림 21 참조). 여기서 볼 수 있듯이 이 서비스는 서비스의 루트 URL 끝에 "collection1"을 추가하여 액세스할 수 있는 "샘플 컬렉션"이라는 컬렉션을 노출합니다. 컬렉션에 액세스하면 서비스는 샘플 컬렉션을 나타내는 Atom 피드를 반환합니다(그림 22 참조). 이 서비스는 표준 AtomPub HTTP 인터페이스를 통해 Atom 항목을 추가, 업데이트 및 삭제할 수도 있습니다.
WCF REST 시작 키트를 사용하여 AtomPub 서비스를 빌드할 때 작업은 노출하려는 논리 컬렉션에 초점을 맞추는 것입니다. 서비스에서 노출하는 비즈니스 엔터티 컬렉션과 AtomPub 컬렉션 간의 매핑을 정의해야 합니다. 이는 기본적으로 사용자 지정 비즈니스 엔터티 클래스와 WCF SyndicationFeed/Item 클래스 간의 매핑 정의로 귀결됩니다.
그림 21: AtomPub 서비스 찾아보기
그림 22: AtomPub 서비스 의해 노출된 샘플 컬렉션으로 검색
HTTP 일반 XML 서비스
모든 REST 제약 조건을 준수하고 전체 HTTP 인터페이스를 지원하여 완전히 RESTful 서비스를 수행할 필요가 없거나 필요하지 않지만 간단한 XML over-HTTP 서비스로 정착하려는 경우에는 방금 설명한 프로젝트 템플릿을 사용하지 않으려는 것입니다. 대신 RESTful 구현을 제공하지 않는 "HTTP 일반 XML WCF 서비스" 프로젝트 템플릿을 확인하려고 합니다. 대신, 시작하는 데 도움이 되는 몇 가지 샘플 XML over-HTTP 작업을 제공합니다.
그림 23은 이 프로젝트 템플릿을 사용할 때 얻을 수 있는 샘플 구현을 보여줍니다. 일부 쿼리 문자열 매개 변수를 입력으로 사용하는 하나의 [WebGet] 작업을 제공하고 XML 엔터티 본문을 수락하고 반환하는 또 다른 [WebInvoke] 작업을 제공하는 방법을 확인합니다. 이러한 작업은 단순히 POX(일반 이전 XML 서비스)를 시작하는 방법을 보여주는 예제로 제공됩니다.
데이터를 반환하는 POX 서비스를 빌드하려는 경우 GetData 메서드를 유지하고 필요에 맞게 요청/응답 데이터를 조정할 수 있습니다. POST 요청을 지원해야 하는 경우 DoWork 메서드를 유지하고 그에 따라 조정할 수 있습니다. 이 특정 프로젝트 템플릿이 다른 프로젝트 템플릿만큼 많은 가치를 제공하지 않도록 이러한 메서드를 전체적으로 다시 작성하게 될 가능성이 있습니다.
[ServiceBehavior(IncludeExceptionDetailInFaults = true),
AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed), ServiceContract]
public partial class Service
{
[WebHelp(Comment = "Sample description for GetData")]
[WebGet(UriTemplate = "GetData?param1={i}¶m2={s}")]
[OperationContract]
public SampleResponseBody GetData(int i, string s)
{
// TODO: Change the sample implementation here
if (i < 0) throw new WebProtocolException(HttpStatusCode.BadRequest,
"param1 cannot be negative", null);
return new SampleResponseBody()
{
Value = String.Format("Sample GetData response: '{0}', '{1}'", i, s)
};
}
[WebHelp(Comment = "Sample description for DoWork")]
[WebInvoke(UriTemplate = "DoWork")]
[OperationContract]
public SampleResponseBody DoWork(SampleRequestBody request)
{
//TODO: Change the sample implementation here
return new SampleResponseBody()
{
Value = String.Format("Sample DoWork response: '{0}'", request.Data)
};
}
}
그림 23: HTTP 일반 XML WCF 서비스 샘플 구현
HttpClient에서 RESTful 서비스 사용
RESTful 서비스 작업의 가장 어려운 측면 중 하나는 클라이언트 쪽 코드를 작성하여 사용하는 것입니다. RESTful 서비스는 WSDL과 유사한 메타데이터를 제공하지 않으므로 클라이언트 쪽 개발자는 대부분의 SOAP 서비스를 프로그래밍 방식으로 통합하기 쉽게 만드는 고급 코드 생성 및 강력한 형식의 프록시 클래스를 즐기지 않습니다. 이러한 현실로 인해 개발자는 RESTful 서비스와 통합하는 것이 일반적인 SOAP 서비스보다 더 번거롭다고 생각하게 됩니다. 내 의견으로는, 이것은 주로 관점의 문제입니다. 키는 프로그래밍할 올바른 클라이언트 쪽 HTTP API를 선택하는 것입니다.
REST 서비스는 단순히 HTTP 기반 서비스이므로 말 그대로 모든 HTTP API를 사용하여 사용할 수 있습니다. 이렇게 하면 클라이언트 쪽에서 많은 유연성을 제공합니다. Microsoft .NET은 HTTP 클라이언트 코드를 프로그래밍하기 위해 WebRequest 및 WebResponse와 같은 System.Net 클래스를 제공합니다. 이러한 클래스는 작업을 완료하지만 클라이언트 쪽 환경을 예상보다 더 복잡하게 만듭니다. SOAP 기반 프록시를 사용하는 데 사용되는 개발자는 일반적으로 매우 매력적이거나 자연스러운 것을 찾지 못합니다.
RESTful 서비스 사용에 대한 클라이언트 쪽 프로그래밍 환경을 간소화하기 위해 WCF REST Starter Kit(미리 보기 2)에는 HTTP 메시지 내에서 찾은 다양한 콘텐츠 형식을 보다 쉽게 처리할 수 있는 다양한 확장 메서드와 함께 균일한 HTTP 인터페이스를 프로그래밍하기 위한 보다 자연스러운 모델을 제공하는 HttpClient라는 새로운 HTTP API가 제공됩니다.
HttpClient 시작
HttpClient 클래스는 HTTP 요청을 보내고 HTTP 응답을 처리하기 위한 간단한 API를 제공합니다. 이 기능은 HttpClient라는 두 가지 기본 클래스 정의와 HttpMethodExtensions라는 두 가지 기본 클래스 정의로 정의됩니다(그림 24 참조). 전자는 클래스의 기본 기능을 정의하고 후자는 다양한 논리적 HTTP 메서드를 대상으로 하는 여러 계층화된 확장 메서드를 제공합니다.
HttpClient 클래스를 검사하는 경우 대상 기본 주소를 지정하는 방법, HTTP 요청 헤더를 조작하는 방법 및 요청을 보내기 위한 수많은 오버로드를 제공합니다. HttpContent 인스턴스를 통해 요청 메시지 콘텐츠를 제공하고 다시 가져오는 HttpResponseMessage 개체를 통해 응답을 처리합니다. 또한 이 클래스는 응답이 돌아올 때까지 기다리는 동안 호출 스레드를 차단하지 않으려는 상황에 대해 비동기 Send 메서드를 제공합니다.
HttpMethodExtensions 클래스를 사용하면 논리적 Get, Post, Put 및 Delete 요청을 실행하기 위해 HttpClient에 여러 확장 메서드를 추가하여 작업을 더욱 쉽게 수행할 수 있습니다. HttpClient에서 RESTful 서비스를 사용할 때 가장 많이 사용할 수 있는 메서드입니다.
public class HttpClient : IDisposable
{
public HttpClient();
public HttpClient(string baseAddress);
public HttpClient(Uri baseAddress);
public Uri BaseAddress { get; set; }
public RequestHeaders DefaultHeaders { get; set; }
public IList<HttpStage> Stages { get; set; }
public HttpWebRequestTransportSettings TransportSettings { get; set; }
public event EventHandler<SendCompletedEventArgs> SendCompleted;
public IAsyncResult BeginSend(HttpRequestMessage request,
AsyncCallback callback, object state);
protected virtual HttpStage CreateTransportStage();
public void Dispose();
protected virtual void Dispose(bool disposing);
public HttpResponseMessage EndSend(IAsyncResult result);
public HttpResponseMessage Send(HttpMethod method);
public HttpResponseMessage Send(HttpRequestMessage request);
public HttpResponseMessage Send(HttpMethod method, string uri);
public HttpResponseMessage Send(HttpMethod method, Uri uri);
public HttpResponseMessage Send(HttpMethod method, string uri,
HttpContent content);
public HttpResponseMessage Send(HttpMethod method, string uri,
RequestHeaders headers);
public HttpResponseMessage Send(HttpMethod method, Uri uri, HttpContent content);
public HttpResponseMessage Send(HttpMethod method, Uri uri,
RequestHeaders headers);
public HttpResponseMessage Send(HttpMethod method, string uri,
RequestHeaders headers, HttpContent content);
public HttpResponseMessage Send(HttpMethod method, Uri uri,
RequestHeaders headers, HttpContent content);
public void SendAsync(HttpRequestMessage request);
public void SendAsync(HttpRequestMessage request, object userState);
public void SendAsyncCancel(object userState);
protected void ThrowIfDisposed();
}
public static class HttpMethodExtensions
{
public static HttpResponseMessage Delete(this HttpClient client, string uri);
public static HttpResponseMessage Delete(this HttpClient client, Uri uri);
public static HttpResponseMessage Get(this HttpClient client);
public static HttpResponseMessage Get(this HttpClient client, string uri);
public static HttpResponseMessage Get(this HttpClient client, Uri uri);
public static HttpResponseMessage Get(this HttpClient client, Uri uri,
HttpQueryString queryString);
public static HttpResponseMessage Get(this HttpClient client, Uri uri,
IEnumerable<KeyValuePair<string, string>> queryString);
public static HttpResponseMessage Head(this HttpClient client, string uri);
public static HttpResponseMessage Head(this HttpClient client, Uri uri);
public static HttpResponseMessage Post(this HttpClient client, string uri,
HttpContent body);
public static HttpResponseMessage Post(this HttpClient client, Uri uri,
HttpContent body);
public static HttpResponseMessage Post(this HttpClient client, string uri,
string contentType, HttpContent body);
public static HttpResponseMessage Post(this HttpClient client, Uri uri,
string contentType, HttpContent body);
public static HttpResponseMessage Put(this HttpClient client, string uri,
HttpContent body);
public static HttpResponseMessage Put(this HttpClient client, Uri uri,
HttpContent body);
public static HttpResponseMessage Put(this HttpClient client, string uri,
string contentType, HttpContent body);
public static HttpResponseMessage Put(this HttpClient client, Uri uri,
string contentType, HttpContent body);
}
그림 24: HttpClient 및 HttpMethodExtensions 클래스 정의
예를 들어 HttpClient가 작업을 간소화하는 방법을 살펴보겠습니다. .NET으로 빌드되지 않은 웹에 있는 실제 RESTful 서비스를 사용합니다. Twitter REST API를 사용합니다.
http://apiwiki.twitter.com/Twitter-API-DocumentationTwitter REST API 설명서를 탐색하는 경우 서비스와 통합하기 위해 적절한 HTTP 요청 발급을 시작하는 방법을 빠르게 알아볼 수 있습니다. 다음 코드는 Twitter 사용자의 "friend timeline"을 검색하는 방법을 보여줍니다.
HttpClient http = new HttpClient("http://twitter.com/statuses/");
http.TransportSettings.Credentials =
new NetworkCredential("{username}", "{password}");
HttpResponseMessage resp = http.Get("friends_timeline.xml");
resp.EnsureStatusIsSuccessful();
ProcessStatuses(resp.Content.ReadAsStream());
HttpClient 인스턴스를 생성할 때 Twitter REST API 서비스의 기본 주소를 제공한 다음 TransportSettings.Credentials 속성을 통해 사용자의 HTTP 자격 증명을 제공합니다. 이제 다양한 Get, Post, Put 및 Delete 메서드를 사용하여 서비스에서 노출하는 다양한 리소스와 상호 작용할 준비가 되었습니다. 이 예제에서는 Get을 호출하여 서비스에서 "friends_timeline.xml" 리소스를 검색합니다. 반환되면 resp 개체에서 EnsureStatusIsSuccessful을 호출하여 200 수준 HTTP 상태 코드를 다시 얻었는지 확인할 수 있습니다.
다음으로, 응답 메시지의 내용을 처리해야 합니다. 이 작업을 수행하는 한 가지 방법은 응답을 스트림으로 읽은 다음 즐겨 찾는 XML API를 사용하여 스트림(ReadAsStream)을 처리하는 것입니다. XmlDocument를 사용하여 응답 XML을 처리하는 방법을 보여 주는 예제는 다음과 같습니다.
static void ProcessStatuses(Stream str)
{
XmlDocument doc = new XmlDocument();
doc.Load(str);
XmlNodeList statuses = doc.SelectNodes("/statuses/status");
foreach (XmlNode n in statuses)
Console.WriteLine("{0}: {1}",
n.SelectSingleNode("user/screen_name").InnerText,
n.SelectSingleNode("text").InnerText);
}
HttpClient 클래스에는 메시지 콘텐츠를 스트림, 문자열 또는 바이트 배열로 처리하는 기능이 함께 제공됩니다. 이 기본 기능 외에도 WCF REST 시작 키트(미리 보기 2)에는 다른 인기 있는 기술(XLinq, XmlSerializer, SyndicationFeed 등)을 사용하여 메시지 콘텐츠를 처리할 수 있도록 하는 수많은 확장 메서드가 포함된 Microsoft.Http.Extensions라는 다른 어셈블리가 함께 제공됩니다. 다음 섹션에서 이러한 확장 메서드의 작동 방식을 살펴보겠습니다.
사용자의 Twitter 상태를 업데이트하는 방법을 보여주는 또 다른 예제를 살펴보겠습니다. 다음 코드를 사용하여 HttpClient를 사용하여 이 작업을 수행할 수 있습니다(HttpClient가 이미 위에서 인스턴스화되어 있다고 가정).
HttpUrlEncodedForm form = new HttpUrlEncodedForm();
form.Add("status", "my first HttpClient app");
resp = http.Post("update.xml", form.CreateHttpContent());
resp.EnsureStatusIsSuccessful();
"update.xml" 리소스에는 POST 요청이 필요하며 메시지에 새 상태 텍스트가 포함된 URL로 인코딩된 문자열이 포함되어 있어야 합니다. HttpClient를 사용하면 HttpUrlEncodedForm 클래스를 통해 URL로 인코딩된 메시지를 쉽게 생성한 다음, 콘텐츠를 제공하는 Post를 호출하기만 하면 됩니다.
이 빠른 예제에서는 HttpClient를 사용하여 GET 및 POST 요청을 쉽게 발급하는 방법을 보여 줍니다. 다음 섹션에서는 HttpClient 세부 정보를 자세히 알아보고 주요 기능 중 일부를 강조 표시합니다.
메시지 콘텐츠 처리
일반적으로 Stream보다 좀 더 정교한 항목을 사용하여 HTTP 응답의 본문을 처리하려고 합니다. WCF REST 시작 키트(미리 보기 2)에는 특정 API를 사용하여 메시지 콘텐츠를 처리할 수 있도록 하는 다양한 확장 메서드를 사용하여 HttpContent 클래스를 향상시키는 Microsoft.Http.Extensions라는 다른 어셈블리가 함께 제공됩니다.
현재 릴리스에서 이러한 확장 메서드는 여러 네임스페이스에 분산되어 있습니다. Microsoft.Http.Extensions.dll대한 참조를 추가하고 적절한 네임스페이스에 using 문을 추가하지 않으면 intellisense에 표시되지 않습니다. 그림 25에는 이 추가 어셈블리에서 사용할 수 있는 HttpContent 확장 메서드가 나열되어 있습니다.
Extension 메서드 | Namespace |
---|---|
ReadAsDataContract |
System.Runtime.Serialization |
ReadAsJsonDataContract |
System.Runtime.Serialization.Json |
ReadAsServiceDocument |
System.ServiceModel.Syndication |
ReadAsSyndicationFeed |
System.ServiceModel.Syndication |
ReadAsXmlReader |
System.Xml |
ReadAsXElement |
System.Xml.Linq |
ReadAsXmlSerializable |
System.Xml.Serialization |
그림 25: HttpContent 확장 메서드
이러한 확장 메서드 중 일부를 사용하는 방법을 보여 주는 몇 가지 예제를 살펴보겠습니다. XLinq 예제는 현재 일반적인 .NET XML 프로그래밍 선택이므로 시작하겠습니다. 콘텐츠 속성에서 ReadAsStream을 호출하는 대신 다음과 같이 ReadAsXElement를 호출합니다.
HttpResponseMessage resp = http.Get("friends_timeline.xml");
resp.EnsureStatusIsSuccessful();
ProcessStatusesAsXElement(resp.Content.ReadAsXElement());
Microsoft.Http.Extensions 어셈블리에 대한 참조가 필요하며 System.Xml.Linq 파일에 using 문을 추가해야 합니다. 이러한 두 단계를 모두 완료한 경우 Content 속성의 intellisense 내에서 ReadAsXElement가 표시됩니다. 다음 예제에서는 메시지 콘텐츠를 XElement로 처리하는 방법을 보여 줍니다.
static void ProcessStatusesAsXElement(XElement root)
{
var statuses = root.Descendants("status");
foreach (XElement status in statuses)
Console.WriteLine("{0}: {1}",
status.Element("user").Element("screen_name").Value,
status.Element("text"));
}
XElement 대신 XmlReader를 사용하여 메시지 콘텐츠를 처리하려는 경우 매우 유사합니다. ReadAsXmlReader를 호출하고 그에 따라 콘텐츠를 처리하기만 하면 됩니다.
XML API를 직접 사용하는 대신, 많은 개발자는 제네릭 XML 노드 대신 강력한 형식의 .NET 형식으로 작업할 수 있는 XML serialization 기술을 사용하는 것을 선호합니다. ReadAsDataContract 및 ReadAsJsonDataContract 확장 메서드를 사용하면 Serialization에 DataContractSerializer(각각 XML 또는 JSON 콘텐츠용)를 활용할 수 있고 ReadAsXmlSerializable을 사용하면 XmlSerializer serialization 엔진을 활용할 수 있습니다.
그러나 이러한 serialization 기반 메서드를 사용하려면 처리하려는 메시지 콘텐츠를 적절하게 모델링하는 일부 .NET 클래스를 획득해야 합니다. 서비스에서 제공하는 XML 스키마 정의 또는 개발 중에 실제로 서비스를 사용하는 동안 검색할 수 있는 일부 샘플 XML에서 적절한 클래스를 생성할 수 있습니다. 그러나 다른 모든 항목이 실패하는 경우 XML 형식에 대한 지식에 따라 항상 이러한 형식을 수동으로 작성할 수 있습니다.
예를 들어 DataContractSerializer를 사용하여 역직렬화를 수행할 때 위의 Twitter에서 가져오는 상태를 모델링하는 몇 가지 C# 클래스는 다음과 같습니다.
[assembly: ContractNamespace("", ClrNamespace = "TwitterShell")]
[CollectionDataContract(Name = "statuses", ItemName = "status")]
public class statusList : List<status> { }
public class user
{
public string id;
public string name;
public string screen_name;
}
public class status
{
public string id;
public string text;
public user user;
}
이러한 클래스를 사용하면 이제 역직렬화 프로세스(이 경우 statusList)에서 사용할 루트 형식을 지정하는 ReadAsDataContract 메서드를 사용할 수 있습니다.
HttpResponseMessage resp = http.Get("friends_timeline.xml");
resp.EnsureStatusIsSuccessful();
ProcessStatusesAsDataContract(resp.Content.ReadAsDataContract<statusList>());
이제 메시지 콘텐츠가 statusList 개체로 역직렬화되었으므로 처리 코드가 다음과 같이 훨씬 간단해집니다.
static void ProcessStatusesAsDataContract(statusList list)
{
foreach (status status in list)
Console.WriteLine("{0}: {1}", status.user.screen_name, status.text);
}
HTTP 응답이 XML 대신 JSON 형식으로 반환되는 경우 다음과 같이 ReadAsJsonDataContract를 호출하면 됩니다.
HttpResponseMessage resp = http.Get("friends_timeline.json");
resp.EnsureStatusIsSuccessful();
ProcessStatusesAsDataContract(resp.Content.ReadAsJsonDataContract<statusList>());
나머지 처리 코드는 동일하게 유지됩니다.
또한 XmlSerializer 엔진을 사용하려면 먼저 XmlSerializer 매핑과 호환되는 직렬화 가능한 형식이 있는지 확인해야 합니다. 이 특정 예제에서는 xml 매핑이 다르기 때문에 statusList 형식을 다음 루트 형식으로 바꿔야 합니다.
public class statuses
{
[XmlElement("status")]
public status[] status;
}
그런 다음 ReadAsDataContract 대신 ReadAsXmlSerializable을 호출하여 역직렬화할 루트 형식의 상태를 지정합니다. 일반적으로 XmlSerializer는 처리할 수 있는 XML 측면에서 DataContractSerializer보다 더 유연합니다. 따라서 XMLSerializer는 RESTful 서비스를 사용하는 데 더 일반적인 선택이 될 가능성이 높습니다. 또한 WCF REST 시작 키트(미리 보기 2)에는 XmlSerializer 형식을 쉽게 생성할 수 있는 Visual Studio 플러그 인이 함께 제공됩니다. 다음 섹션에서는 작동 방식을 설명합니다.
마지막 예로 서비스가 Atom 피드를 반환하면 어떻게 되나요? 이 경우 ReadAsSyndicationFeed를 호출한 다음 SyndicationFeed 개체를 다시 가져와서 처리할 수 있습니다.
HttpResponseMessage resp = http.Get("friends_timeline.atom");
resp.EnsureStatusIsSuccessful();
ProcessStatusesAsFeed(resp.Content.ReadAsSyndicationFeed());
그런 다음 SyndicationFeed 개체를 처리하여 관심 있는 정보를 추출할 수 있습니다(정보가 Atom 피드 구조 내에 있는 위치를 알아야 합니다.)
static void ProcessStatusesAsFeed(SyndicationFeed feed)
{
foreach (SyndicationItem item in feed.Items)
Console.WriteLine("{0}: {1}", item.Authors[0].Name, item.Title.Text);
}
보듯이 새 HttpClient 클래스를 사용하면 .NET에서 RESTful 서비스를 매우 쉽게 사용할 수 있습니다. Microsoft.Http.Extensions에 있는 새로운 확장 메서드는 메시지 처리에 대한 많은 유연성을 제공하고 가장 일반적인 REST 시나리오(XML, JSON 및 Atom 등)를 간소화합니다.
Visual Studio에서 "XML을 형식으로 붙여넣기"
RESTful 서비스는 WSDL과 함께 제공되지 않으므로 SOAP에 익숙한 것처럼 강력한 형식의 프록시를 생성할 수 없습니다. 그러나 모든 REST 서비스가 동일한 서비스 계약(HTTP 균일 인터페이스)을 구현하므로 HttpClient에서 제공하는 작업(가져오기, 게시, 배치 및 삭제) 외에는 아무것도 필요하지 않습니다. 그러나 이전 섹션에서 수행하는 방법을 보여 준 HTTP 메시지 처리 논리를 간소화하기 위해 몇 가지 직렬화 가능한 형식을 원할 수 있습니다.
문제의 서비스가 콘텐츠를 설명하는 XML 스키마 정의를 제공하는 경우(예: 자동 "도움말" 페이지에서 서비스 쪽에서 WCF REST 시작 키트를 제공함) xsd.exe 또는 svcutil.exe 같은 도구를 사용하여 적절한 형식을 생성할 수 있습니다. 그렇지 않은 경우 "XML을 형식으로 붙여넣기"라는 WCF REST 시작 키트(미리 보기 2)에서 도입한 새 Visual Studio 플러그 인을 활용할 수 있습니다.
이 새로운 기능을 사용하면 XML 스키마 정의 또는 샘플 XML 인스턴스를 클립보드에 복사한 다음 편집 메뉴에서 "XML을 형식으로 붙여넣기"를 선택할 수 있습니다. 이렇게 하면 클립보드의 스키마/XML에 적합한 XmlSerializer 형식을 생성하고 파일의 현재 위치에 붙여넣습니다. XML 샘플 인스턴스를 사용하는 경우 항상 완벽하게 정확한 형식을 생성할 수 없으므로 생성 후 몇 가지 추가 마사지를 수행해야 할 수 있습니다.
이를 Twitter 예제와 함께 사용하는 방법을 살펴보겠습니다. 브라우저(http://twitter.com/statuses/friends\_timeline.xml)를 사용하여 Twitter "friends_timeline.xml" 리소스로 이동하면 이 리소스에서 반환된 실제 XML을 다시 가져옵니다(그림 26 참조). 이제 "원본 보기"를 수행하고 XML을 클립보드에 복사합니다. 복사한 후에는 Visual Studio로 돌아가 생성된 형식을 이동하려는 위치에 커서를 놓을 수 있습니다. 그런 다음 편집 메뉴에서 "XML을 형식으로 붙여넣기"를 선택하면(그림 27 참조) 필요한 XmlSerializer 형식이 파일에 추가됩니다. 일단 준비되면 ReadAsXmlSerializable과 함께 이러한 형식을 사용하여 메시지 콘텐츠를 처리할 수 있습니다.
서비스를 구현할 때 WCF REST 시작 키트에서 제공하는 "도움말" 페이지와 함께 이 기능을 사용할 수 있습니다. 개발 중에 도움말 페이지를 찾아보고, 다양한 리소스 및 작업에 대한 스키마 또는 예제 XML 메시지로 이동하고, 이 플러그 인을 사용하여 클라이언트 애플리케이션 내에서 적절한 메시지 유형을 생성할 수 있습니다. 일반적으로 RESTful 서비스와 통합하려는 클라이언트의 기준이 낮아지고 개발자 환경이 간소화됩니다.
그림 26: Twitter friends_timeline.xml 리소스 반환된 샘플 XML
그림 27: XML을 형식 메뉴 항목으로 붙여넣기
서비스 입력 처리
이전 섹션에서는 HTTP 응답 메시지에서 찾은 메시지 콘텐츠를 처리하는 데 집중했습니다. RESTful 서비스에서 예상하는 적절한 입력을 생성하는 방법도 고려해야 합니다. 대부분의 서비스는 쿼리 문자열, URL로 인코딩된 양식 데이터 또는 HTTP 요청 엔터티 본문(예: XML, JSON, Atom 등) 내에서 사용할 수 있는 다양한 형식을 통해 입력을 허용합니다.
서비스에 쿼리 문자열 입력이 필요한 경우 HttpQueryString 클래스를 사용하여 제대로 빌드할 수 있습니다. 다음 예제에서는 Get 메서드에 제공할 수 있는 쿼리 문자열을 작성하는 방법을 보여줍니다.
HttpQueryString vars = new HttpQueryString();
vars.Add("id", screenname);
vars.Add("count", count);
resp = http.Get(new Uri("user_timeline.xml", UriKind.Relative), vars);
resp.EnsureStatusIsSuccessful();
DisplayTwitterStatuses(resp.Content.ReadAsXElement());
서비스에 URL로 인코딩된 양식 입력(예: HTML <양식> 제출에서 가져오는 항목)이 필요한 경우 HttpUrlEncodedForm 또는 HttpMultipartMimeForm 클래스를 사용하여 적절한 콘텐츠를 빌드할 수 있습니다(다중 파트 MIME 양식을 생성해야 하는 경우 나중에 사용). 다음 예제에서는 Post 메서드에 제공할 수 있는 간단한 양식 제출을 빌드하는 방법을 보여 줍니다.
HttpUrlEncodedForm form = new HttpUrlEncodedForm();
form.Add("status", status);
resp = http.Post("update.xml", form.CreateHttpContent());
resp.EnsureStatusIsSuccessful();
Console.WriteLine("Status updated!");
이러한 클래스 외에도 Microsoft.Http.Extensions 어셈블리에는 HttpClient에 입력으로 제공할 수 있는 HttpContent 개체를 생성하는 프로세스를 간소화하는 몇 가지 추가 확장 메서드가 함께 제공됩니다. 이러한 메서드는 HttpContentExtensions 클래스에 정의되며 Create(XElement에서), CreateDataContract, CreateJsonDataContract, CreateXmlSerializable, CreateAtom10SyndicationFeed 및 CreateRss20SyndicationFeed와 같은 항목을 포함합니다. HTTP 요청 메시지에 대해 이러한 형식의 콘텐츠 중 하나를 생성해야 하는 경우 이러한 메서드를 사용하려고 합니다.
형식화된 헤더를 사용하여 헤더 처리 간소화
Microsoft.Http 어셈블리에는 강력한 형식의 HTTP 헤더 클래스 제품군도 함께 제공됩니다. Microsoft.Http.Headers 네임스페이스 내에서 찾을 수 있습니다. 예를 들어 CacheControl, Connection, Cookie, Credential, EntityTag, Expect 등의 클래스를 다른 클래스와 함께 찾을 수 있습니다.
HttpClient 클래스는 이러한 헤더 형식을 통해 요청 헤더를 노출하는 DefaultHeaders 속성을 제공합니다. 요청을 보내기 전에 개별 헤더를 조작할 수 있습니다. HttpResponseMessage 클래스에는 이러한 헤더 클래스를 통해 다양한 HTTP 응답 헤더를 다시 노출하는 Headers 속성도 함께 제공됩니다. 다음 예제에서는 조건부 GET 요청을 발급하기 위해 요청/응답에서 헤더를 조작하는 방법을 보여 줍니다.
HttpResponseMessage resp = http.Get("public_timeline.atom");
resp.EnsureStatusIsSuccessful();
ProcessStatusesAsFeed(resp.Content.ReadAsSyndicationFeed());
DateTime? lastAccessDate = resp.Headers.Date;
...
http.DefaultHeaders.IfModifiedSince = lastAccessDate;
resp = http.Get("public_timeline.atom");
Console.WriteLine("status={0}", resp.StatusCode);
강력한 형식의 HTTP 헤더 클래스를 사용하면 기본 HTTP 프로토콜 세부 정보의 여러 측면으로부터 보호되므로 코드에서 HTTP 헤더를 더 쉽게 사용할 수 있습니다.
HttpClient "Stage" 처리
HttpClient 클래스에는 서비스 쪽의 WebServiceHost2에서 제공하는 RequestInterceptor 모델과 유사하게 사용자 지정 코드를 연결할 수 있는 요청 가로채기 메커니즘이 함께 제공됩니다. 이 요청 가로채기 모델은 클라이언트 쪽 HTTP 상호 작용을 위해 특별히 설계되었습니다.
작동 방식은 다음과 같습니다. HttpClient 클래스는 HttpStage 클래스에서 모델링되는 "HTTP 처리 단계" 컬렉션을 관리합니다. 메시지를 보내기 전에 HttpClient 인스턴스를 사용하여 HttpStage 파생 개체의 컬렉션을 구성합니다. 그런 다음 HTTP 요청을 실행할 때마다 HttpClient 인스턴스가 각 HttpStage 개체를 호출하여 처리를 수행할 수 있습니다.
각각 동기 및 비동기 모델을 제공하는 HttpStage, HttpProcessingStage 및 HttpAsyncStage에서 파생되는 몇 가지 클래스가 있습니다. 사용자 고유의 사용자 지정 단계를 구현할 때 일반적으로 이러한 두 클래스 중 하나에서 파생됩니다. 이러한 클래스에서 파생되는 경우 논리를 정의하기 위해 ProcessRequest 및 ProcessResponse 메서드를 재정의하는 것이 작업입니다.
다음 코드에서는 단순히 콘솔 창에 메시지를 인쇄하는 사용자 지정 HttpProcessingStage를 구현하는 방법을 보여 줍니다.
public class MyHttpStage : HttpProcessingStage
{
public override void ProcessRequest(HttpRequestMessage request)
{
Console.WriteLine("ProcessRequest called: {0} {1}",
request.Method, request.Uri);
}
public override void ProcessResponse(HttpResponseMessage response)
{
Console.WriteLine("ProcessResponse called: {0}",
response.StatusCode);
}
}
사용자 지정 HttpStage 파생 클래스를 구현한 후에는 HttpClient를 사용하는 동안 이를 활용할 수 있습니다. 다음 예제에서는 HTTP 요청을 실행하기 전에 MyHttpStage 인스턴스를 혼합에 추가하는 방법을 보여 줍니다.
HttpClient http = new HttpClient("http://twitter.com/statuses/");
http.TransportSettings.Credentials =
new NetworkCredential("skonnarddemo", "baby95");
// configure the custom stage
http.Stages.Add(new MyHttpStage());
HttpResponseMessage resp = http.Get("public_timeline.atom");
이제 Get 메서드를 호출하면 대상 서비스에 대한 HTTP 요청을 실행하기 전과 후에 콘솔 창에 인쇄된 MyHttpStage 메시지가 표시됩니다. 이 가로채기 기술을 사용하여 HTTP 클라이언트 애플리케이션에서 더 많은 재사용성을 촉진하는 방식으로 다양한 클라이언트 쪽 HTTP 처리 요구 사항(예: 보안, 로깅, 추적, 사용자 지정 캐싱 등)을 구현할 수 있습니다.
특수 클라이언트를 만들기 위해 HttpClient 확장
HttpStage 확장성 메커니즘 외에도 HttpClient에서 파생하여 고유한 특수 REST 클라이언트 라이브러리를 만들 수도 있습니다. 이를 통해 RESTful 서비스의 사용자에게 더 적합한 메서드와 속성을 제공할 수 있으며, 궁극적으로 일반적인 SOAP 프록시 클래스 환경과 유사하게(초과하지 않음) 개발자 환경을 제공할 수 있습니다.
WCF REST 시작 키트(미리 보기 2)의 일부로 AtomPubClient라는 특수 HttpClient 파생 클래스의 예를 제공합니다. 표준 AtomPub 서비스와 상호 작용하기 위한 사용자 지정된 클라이언트 쪽 환경을 제공합니다. AtomPubClient 클래스 정의는 다음과 같습니다.
public class AtomPubClient : HttpClient
{
public AtomPubClient();
public SyndicationItem AddEntry(SyndicationFeed feed, SyndicationItem newEntry);
public SyndicationItem AddEntry(Uri feedUri, SyndicationItem newEntry);
public SyndicationItem AddMediaResource(SyndicationFeed feed, string contentType,
string description, HttpContent mediaContent);
public SyndicationItem AddMediaResource(Uri mediaCollectionUri,
string contentType, string description, HttpContent mediaContent);
public void DeleteEntry(SyndicationItem entry);
public void DeleteEntry(Uri itemUri);
public SyndicationItem GetEntry(Uri itemUri);
public SyndicationFeed GetFeed(Uri feedUri);
public ServiceDocument GetServiceDocument(Uri serviceDocumentUri);
public SyndicationItem UpdateEntry(SyndicationItem oldValue,
SyndicationItem newValue);
public SyndicationItem UpdateEntry(Uri editUri, SyndicationItem newValue);
}
AtomPub 서비스와 관련된 AddEntry, GetEntry, UpdateEntry, GetFeed 등의 메서드를 제공하는 방법을 알아봅니다. 이는 HttpClient에서 기본 Get, Post, Put 및 Delete 메서드를 사용하는 것보다 이러한 유형의 서비스와 상호 작용하는 방법에 대해 생각하는 훨씬 더 자연스러운 방법입니다.
다음 코드 예제에서는 AtomPubClient 클래스를 사용하여 AtomPub 서비스를 탐색하고 첫 번째 작업 영역 컬렉션에 새 Atom 항목을 추가하는 방법을 보여 줍니다.
AtomPubClient client = new AtomPubClient();
ServiceDocument doc = client.GetServiceDocument(
new Uri("https://localhost:30807/Service.svc/"));
Uri feedUri = doc.Workspaces[0].Collections[0].Link;
SyndicationFeed feed = client.GetFeed(feedUri);
SyndicationItem item = new SyndicationItem()
{
Title = new TextSyndicationContent("New Item"),
PublishDate = DateTime.Now
};
client.AddEntry(feed, item);
또한 WCF REST 시작 키트(미리 보기 2)에는 서비스 리소스를 "폴링"하고 리소스가 변경될 때만 작업을 수행하기 위한 클라이언트 논리를 보다 쉽게 구현할 수 있는 PollingAgent라는 클래스가 함께 제공됩니다. 실제로 HTTP 작업을 수행하는 HttpClient 개체와 함께 PollingAgent를 사용합니다. 다음은 PollingAgent에 대한 전체 클래스 정의입니다.
public class PollingAgent : IDisposable
{
public PollingAgent();
public HttpClient HttpClient { get; set; }
public bool IgnoreExpiresHeader { get; set; }
public bool IgnoreNonOKStatusCodes { get; set; }
public bool IgnoreSendErrors { get; set; }
public TimeSpan PollingInterval { get; set; }
public event EventHandler<ConditionalGetEventArgs> ResourceChanged;
public void Dispose();
public void StartPolling();
public void StartPolling(Uri uri);
public void StartPolling(Uri uri, EntityTag etag, DateTime? lastModifiedTime);
public void StopPolling();
}
따라서 PollingAgent 클래스의 인스턴스를 만들고 사용할 HttpClient 개체를 제공합니다. 그런 다음 ResourceChanged 이벤트에 대한 콜백 메서드를 설정하고 폴링 간격을 지정합니다. 모든 작업이 완료되면 StartPolling을 호출하고 대상 리소스 URI를 제공합니다. 다음 코드 샘플에서는 Twitter 공개 타임라인을 "폴링"하도록 설정하는 방법을 보여 줍니다.
class Program
{
static void Main(string[] args)
{
PollingAgent pollingClient = new PollingAgent();
pollingClient.HttpClient = new HttpClient();
pollingClient.HttpClient.TransportSettings.Credentials =
new NetworkCredential("skonnarddemo", "baby95");
pollingClient.PollingInterval = TimeSpan.FromSeconds(10);
pollingClient.ResourceChanged += new EventHandler<ConditionalGetEventArgs>(
pollingClient_ResourceChanged);
pollingClient.StartPolling(
new Uri("http://twitter.com/statuses/public_timeline.xml"));
Console.WriteLine("polling...");
Console.ReadLine();
}
static void pollingClient_ResourceChanged(object s, ConditionalGetEventArgs e)
{
ProcessStatusesAsXElement(e.Response.Content.ReadAsXElement());
}
static void ProcessStatusesAsXElement(XElement root)
{
var statuses = root.Descendants("status");
foreach (XElement status in statuses)
Console.WriteLine("{0}: {1}",
status.Element("user").Element("screen_name").Value,
status.Element("text"));
}
}
이러한 예제에서 볼 수 있듯이 HttpClient는 보다 특수한 클라이언트 쪽 프로그래밍 모델을 제공하도록 확장할 수 있는 간단한 HTTP 기반을 제공합니다. 자신의 HttpClient 파생 클래스를 빌드하는 데 약간의 시간을 투자하면 클라이언트 쪽 개발자 환경 측면에서 큰 배당금을 지불합니다.
결론
Microsoft는 Microsoft .NET 프레임워크를 사용하여 RESTful 서비스를 구현하고 사용하기 위한 일류 프로그래밍 모델을 제공하기 위해 노력하고 있습니다. WCF 3.5는 RESTful 서비스를 빌드하는 데 필요한 기본 "웹" 프로그래밍 모델을 도입했지만 시작에 불과했습니다. WCF REST 스타터 키트는 REST 중심 개발 작업을 간소화하기 위해 특별히 설계된 WCF 확장 및 주요 Visual Studio 통합 세트를 제공하는 Microsoft 후원 CodePlex 프로젝트입니다. 오늘날 WCF REST 스타터 키트에 있는 많은 기능은 최신 버전의 .NET Framework로 전환될 가능성이 높지만 기다릴 이유가 없습니다. 이러한 기능을 오늘 사용하기 시작할 수 있습니다.
TheAuthor 정보
Aaron Skonnard는 강사 주도 및 주문형 개발자 과정을 모두 제공하는 Microsoft 교육 공급자인 Pluralsight의 공동 창립자입니다. 요즘 아론은 Pluralsight On-Demand를 녹음하는 데 대부분의 시간을 보냅니다! 에서는 클라우드 컴퓨팅, Windows Azure, WCF 및 REST에 중점을 두는 과정을 진행합니다. Aaron은 전 세계의 전문 개발자를 쓰고, 말하고, 가르치는 데 수년을 보냈습니다. 당신은 http://pluralsight.com/aaronhttp://twitter.com/skonnard그에게 도달 할 수 있습니다.
추가 리소스
- 아키텍처 스타일 및 Roy Fielding의 네트워크 기반 소프트웨어 아키텍처 디자인
- 레너드 리처드슨의 RESTful 웹 서비스(오라일리) & 샘 루비
- 존 플랑드르의 RESTful .NET(오라일리)
- MSDN WCF 개발자 센터(WCF REST 시작 키트 포함) REST를
- Pluralsight의 RESTful .NET 기본 교육 과정