블로그 이미지
Sunny's

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2011. 7. 12. 13:07 ASP.NET
Just return a JsonResult.
Here is an example I am using in production:
public partial class StudentController : BaseController {
    public StudentController(RESTContext portalContext)
        : base(portalContext) { }

    [HttpGet, Url("organizations/{organizationId?}/students")]
    public virtual JsonResult List(Guid? organizationId) {
        if (organizationId != RESTContext.OrganizationId)
            throw new HttpNotAuthorizedException();

        var query = RESTContext.GetQuery<IQuery<StudentCasesReport>>()
            .Where(x => x.OrganizationId, organizationId)
            .OrderBy(x => x.LastName, SortOrder.Ascending);
        var cases = query.Execute(IsolationLevel.ReadUncommitted);

        return Json(cases, JsonRequestBehavior.AllowGet);
    }

    [HttpGet, Url("organizations/{organizationId?}/students/{studentId?}")]
    public virtual JsonResult Get(Guid? organizationId, Guid? studentId) {
        if (studentId.IsNull())
            throw new HttpNotFoundExecption();

        if (organizationId != RESTContext.OrganizationId)
            throw new HttpNotModifiedException();

        var query = RESTContext.GetQuery<IQuery<StudentCasesReport>>()
            .Where(x => x.OrganizationId, organizationId)
            .Where(x => x.StudentCaseId, studentId)
            .OrderBy(x => x.LastName, SortOrder.Ascending);
        var cases = query.Execute(IsolationLevel.ReadUncommitted).FirstOrDefault();

        if (cases.IsNull())
            throw new HttpNotFoundExecption();

        return Json(cases, JsonRequestBehavior.AllowGet);
    }
}
posted by Sunny's
2011. 5. 12. 19:51 ASP.NET



  1. Routing Service in WCF
  2. Service Discovery(Ad-hoc)
  3. Web Service Programming(REST)
 
WCF 서비스는 기본적으로 ASP.NET 기능을 지원하지 않습니다만 , 제한적으로 Aspnet Compatibility Mode 를 이용해서 부분적으로 ASP.NET 기능을 활성화 시킬수 있습니다. 이 부분은 WCF 에서 쿠키사용하기 (http://www.hoons.kr/Lecture/LectureView.aspx?BoardIdx=20574&kind=32) 를 참조하시기 바랍니다. 
 
WCF 4.0 에서는 WebProtocolException , RequestInterceptor , WebCacheAttribute 클래스를 추가하여 웹 환경에서의 스팩을 보다 강화하였습니다. 또한 POST , GET 외에 PUT , DELETE 도 추가로 지원하여 , 그간 반쪽짜리로 구현되었던 REST 를 완벽하게 지원하게 되었습니다. 

* REST 서비스에 대한 개념과 기존 REST 구현은 여기를 참조해주세요(http://www.hoons.kr/Lecture/LectureView.aspx?BoardIdx=375&kind=32
 


그럼 이제 WCF 4.0 에서의 REST 서비스를 구현해 보도록 하겠습니다. 
 
먼저 WCF 서비스를 생성한후 web.config 를 아래와 같이 설정합니다. 
<?xml version="1.0"?> 
<configuration
<system.web> 
<compilation debug="true" targetFramework="4.0" /> 
</system.web> 
<system.serviceModel>  
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"></serviceHostingEnvironment> 
<services> 
<service name="WCFRest.Service1"
<endpoint address="" binding="webHttpBinding" contract="WCFRest.IService1" behaviorConfiguration="EdpHelpBehavior"> 
</endpoint> 
</service> 
</services> 
<behaviors>  
<endpointBehaviors>  
<behavior name="EdpHelpBehavior"> 
<webHttp helpEnabled="true"/>  
</behavior> 
</endpointBehaviors> 
</behaviors> 
</system.serviceModel>  
<system.webServer> 
<modules runAllManagedModulesForAllRequests="true"/> 
</system.webServer> 
</configuration>
[코드1. REST 서비스 설정을 위한 web.config ] 
 
노란색으로 표기된 부분이 REST 서비스를 사용하기 위해 설정해야 하는 부분입니다. 이는 기존 3.5에 있던 방식과 동일합니다. 
 
이제 프로젝트를 빌드하고 서비스를 열어보겠습니다. 
 

[그림1. REST 구동 화면
 
4.0 에서는 새롭게 REST 서비스에 대해 호출여부를 판별할수 있는 help 페이지를 제공합니다. 


[그림2. REST Help 페이지
 
헬프 페이지는 간략하게 자신이 생성한 WCF 서비스를 REST 로 호출할수 있게 해줍니다. 
 


[그림3. REST 서비스 실행 결과
 
해당 help 페이지 대로 호출한 결과입니다. 정상적으로 REST 서비스가 동작함을 확인할수 있습니다. 
 

WebCacheAttribute 
 
이제 WCF 에서도 페이지단위로 캐싱된 결과물을 내려줄수 있습니다. 
 
System.Web 엘리먼트 안에 캐시 관련 항목을 삽입합니다. 
 
<system.web> 
<compilation debug="true" targetFramework="4.0" /> 
<caching> 
<outputCacheSettings> 
<outputCacheProfiles> 
<add name="CacheResponse" duration="60" varyByParam="format"/> 
</outputCacheProfiles> 
</outputCacheSettings> 
</caching> 
</system.web>
[코드2 . 캐싱 적용을 위한 web.config 설정
 
코드에 다음 어트리뷰트를 삽입합니다. 해당 코드에서는 캐쉬 시간을 60초로 설정하였습니다. 

[OperationContract
[WebGet
[AspNetCacheProfile("CacheResponse")] 
string GetData(int value); 
[코드3 .WCF 코드에 Cache 설정
 
캐싱에 사용될 설정을 가져옵니다. 
 
 

[그림4 .캐싱이 적용되지 않은 페이지 헤더
 
캐싱이 적용되지 않은 페이지의 헤더 정보 입니다. 

 

[그림5 .캐싱이 적용된 페이지 헤더
  
캐싱이 적용된 후 헤더입니다. Cache-Control 에 max-age=60 값이 추가된 것을 확인할수 있습니다. 
 

Summary 

이상으로 WCF 4.0 에 추가된 기능들에 대해 간략하게 알아보았습니다. WCF 3.5 에 비해 진입장벽은 현저히 낮아졌으며 , 추가 기능들은 매우 강력합니다. 이전에 MS 측 관계자와 asmx 서비스를 더 이상 사용하지 않는 방안에 대해 살짝 논의한적이 있었습니다. (공식적인 입장은 아닙니다.) WCF 서비스는 더 이상 asmx 의 대체적인 성격이 아닌 , asmx 의 차세대 버전이라고 생각해도 좋을거 같습니다. 흥미로운 기능들이 많이 포함된 새로운 WCF를 한번 즐겨보시기 바랍니다 ^^ 
-fin 

출처 :  http://loveciel.tistory.com/entry/What’s-new-in-WCF-40
posted by Sunny's
2011. 5. 12. 14:04 ASP.NET

출처 : http://www.cooolguy.net/153

REST 아키텍처 스타일에 대해서는 앞의 글에서도 소개했었는데, 오늘은 WCF 서비스를 활용한 방법에 대해 이야기하려고 합니다. REST(Representational State Transfer)에 대해서는 많은 분들이 도대체 뭐야? 라는 반응을 보이십니다.
하지만, 앞으로 정말 많이, 여러번 듣게 되실 개념이기 때문에 꼭 이해가 필요합니다.

WOA(Web Oriented Architecture)가 결국은 RESTful 아키텍처를 의미합니다.

REST란?
아키텍처 스타일이란 말은 어떤 무엇인가를 구축할 때 적용되는 규약들의 집합이라고 이야기할 수 있습니다. 즉, 소프트웨어 시스템을 구현할 때 사용되는 가이드 정도로 이해하면 되겠습니다.

클라이언트가 서비스(Endpoint)를 요청할 때 사용되는 클라이언트 – 서버 아키텍처 스타일 중의 하나입니다.
HTTP 스펙을 만든 사람 중의 한 명인 Roy Fielding 교수가 박사학위 논문에서 사용한 개념 입니다.
Architectural Styles and the Design of Network-based Software Architectures

Web이 바로 REST 아키텍처 스타일을 그대로 따르고 있는 시스템 입니다. 원칙을 한 번 살펴보도록 하죠.

  • 사용자 에이전트가 자원과 상호작용하는데, 자원은 URI로 표현될 수 있는 모든 것들이 대상입니다.
  • 자원과의 상호작용은 HTTP의 표준 동사(GET, POST, PUT, and DELETE)를 사용하여 이루어집니다. 또한, 상호작용을 위해 자원의 미디어타입이 결정되는데 HTTP 컨텐츠 타입의 헤더를 사용합니다.
    (XHTML, XML, JPG, PNG, and JSON are some well-known media types.)
  • 자원은 필요한 모든 정보를 다 포함하고 있습니다. (서비스가 stateless 형태로 존재)
  • 자원은 다른 자원과의 링크를 포함합니다. (하이퍼미디어)

하나의 간단한 예제로 이해를 해보도록 하죠.

MSDN 매거진의 데이터을 처리하는 서비스를 만든다고 가정해보죠.
- 해당 서비스는 지금까지 발행된 모든 MSDN 매거진에 대한 정보를 알려줍니다.
  매거진의 편집자가 이 서비스를 쓸 것이고 새로 발행되는 매거진에 대한 정보를 추가하고, 관리하는 등의 작업을 할
   예정 입니다.

RESTful 서비스를 구축할 때 서비스 디자인 단계를 살펴보죠

  1. 자원은 어떤 것들이 될 것인지?
  2. 자원을 표현하는데 사용할 URI로 어떤 값을 쓸 것인지?
  3. 단일 인터페이스 (HTTP 동사) 중 어떤 것을 쓸 것인지?

RESTful endpoint를 만드는 기본적인 블럭들입니다. 자원, 그리고 표현 방법

자원: MSDN 잡지가 출간된 모든 년도, 매년 발행된 이슈, 매거진에 게재된 기사
해당 자원을 표현하기 위해 XML을 사용하기로 하죠, 하지만 RESTful서비스는 XML에 한정된 것은 아니고 여러 유형으로 표현 가능합니다.

이제 URI를 결정해보죠. 절대경로는 이 endpoint를 어떤 호스트에 배치할 것인지에 달라지기 때문에, 여기서는 상대경로로 표현하도록 하겠습니다.
모든 년도들의 리스트, 즉 서비스의 루트 URI로 / 를 사용합니다.
이 구문을 활용하면, /{year} 는 해당 년도에 발행된 모든 잡지들에 대한 정보를 리턴합니다.
/{year}/{issue}는 해당 년도에 발행된 특정한 이슈에 대한 값을 리턴하겠죠.
/{year}/{issue}/{article}은 해당 이슈의 기사에 대한 정보값을 리턴하는데, 각 기사는 1 .. n까지 번호가 매겨졌다고 가정하죠

자, 이제 해당 URI를 단일인터페이스 (HTTP 동사)와 연결해보죠. 해당 잡지의 히스토리는 읽기 전용이어야 하므로, 해당 root 자원은 GET 동사만 사용되어야 합니다. 새로운 년도는 추가될 수 있으므로 PUT을 통해 /{year}가 추가되고, PUT을 통해 수정도 이루어질 수 있습니다. POST 연산을 통해서는 해당 자원을 신규로 생성할 때 사용되는데 해당 이슈를 만드는, /{year}/{issue} 등으로 사용합니다.

예를들어, 2006년 1월 이슈의 모든 기사를 원한다면 HTTP GET /2006/January 라고 가져올 수 있을 것이고 2009년 3월 이슈를 만들려면 POST /2008/december라고 쓸 수 있다는 것이죠.

그렇다면 왜 REST에 대해 이야기를 할까요?
서비스를 사용하는 방법이 REST 밖에 없을까요? 그렇지 않습니다.

클라이언트 서버 스타링의 애플리케이션을 구현하는데 있어서 다른 아키텍처 스타일을 사용할 수 있습니다. .NET의 특화된 RPC(Remote Procedure Call), 즉 DCOM 및 .NET 리모팅 등을 사용하거나 상호운용성이 제공되는 ASMX를 사용하는 SOAP, WCF 등의 RPC 기술을 사용할 수 있었죠.
그런데, REST를 사용하면 첫째, 훨씬 간편하고 쉽게 서비스를 구현할 수 있고 둘째, 마이크로소프트가 RPC 기술(SOAP 포함)에 비해 REST 아키텍처 스타일을 훨씬 더 선호하고 있기 때문인데, 그 이유가 있다는 거죠. 마이크로소프트의 클라우드 컴퓨팅, 애저 서비스 플랫폼과 윈도 애저에서도 REST 스타일을 채택하고 있습니다.

장점
Caching HTTP 동사를 통해 RESTfu endpoint가 불리워 질 때, HTTP 동사 GET을 사용합니다. GET으로 불리워진 자원들은 여러 다양한 방법으로 캐싱이 가능합니다. 결국 속도와 확장성이 향상됩니다.

Scale-Out REST는 각 자원이 특정 요청에 필요한 모든 상태 값을 다 가지고 있기 때문에, 즉 stateless기 때문에 확장이 훨씬 용이합니다.

Idempotent GET을 이용해 자원을 요청할 때 추가적인 부작용이 벌어지지 않습니다 (즉, 다시 한 번 GET, DELETE 등을 불러도 두번 중복작업이 이루어지지 않음)

Interoperability SOAP이 클라이언트 서버 프로그램을 통해 상호운용성이 뛰어나다고 하지만, 몇 몇 언어 및 환경은 여전히 SOAP 툴킷을 갖고 있지 못하고, 툴킷을 가지고 있는 언어의 경우에도 최신 표중르 가진 다른 툴킷과 통신할 수 없는 경우가 종종 발생합니다. REST는 HTTP 동사만을 사용하기 때문에 어떤 언어, 플랫폼과도 상호운용이 가능합니다.

Simplicity 너무 단순하게 구현이 가능합니다. URI와 HTTP 동사는 모든 웹개발자들이 다 알고 있는 내용입니다.

가능한 경우에 REST를 많이 사용하시기 바라고, 엔터프라이즈에서도 고려하고 있는 중입니다.

WCF and REST

WCF는 스타일, 프로토콜에 무관하게 통신이 가능한 분산 컴퓨팅 환경의 애플리케이션을 구축하는데 사용되는 마이크로소프트의 프레임웍 입니다. WCF 개념은 개발자가 한 프로그래밍 언어를 통해 다른 많은 분산 시스템을 사용 가능하도록 돕는 기술 입니다.

이전 버전의 WCF는 RPC(SOAP 등)를 이용하도록 사용되었는데, .NET Framwork 3.0부터는 REST 서비스를 사용할 수 있도록 지원하고 있었는데 REST 기반 서비스 프로그래밍이 가능한 모델 및 인프라 지원이 부족했는데, .NET Framwork 3.5 SP1에 프로그래밍 모델 및 기타 많은 부분이 개선되었습니다. 저도 조금 살펴봤는데 잘 만들어졌습니다.

프로그래밍 모델 중 두가지 속성, WebGetAttribute 과 WebInvokeAttribute, URI template 메커니즘이 주목할 만합니다.
URI, 메쏘드에 적용될 동사를 정의할 수 있도록 해주는 거죠. 인프라 영역에서는 WebHttpBinding 바인딩과 REST가 적절한 네트웍 스택을 사용할 수 있도록 WebHttp­Behavior가 지원됩니다. 또한, 커스톰 Service­Host (WebServiceHost) 와 ServiceHostFactory (WebServiceHostFactory)가 포함되어 있습니다.

WebGetAttribute 와 WebInvokeAttribute

WCF는 연결된 시스템 구축을 간단하게 해주는데, 네트웍 메시지를 여러분이 서비스를 구현하기 위해 정의한 클래스의 인스턴스로 라우팅해버린다. 결국, 네트웍 트랙픽을 처리하기 위한 인프라에 신경쓰지 않고, 코드의 로직에 집중할 수 있게 해주는 거죠. WCF와 인프라를 함께 사용할 때 기본 디스패처는 요청에서 사용하는 URI를 기준으로 라우팅을 진행합니다. 즉, 이 라우팅 방식을 사용하면 RESTful endpoint를 아주 쉽게 구현할 수 있고, 실제로 WebHttpDispatchOperationSelector를 사용합니다. 

이것이 가능하도록 하는 핵심은 WebHttpDispatch­OperationSelector는 서로 다른 URI와 동사를 여러분이 만든 메쏘드에 어떻게 매핑할 것인지를 아는 거죠. WebGetAttribute 와 WebInvokeAttribute는 WCF ServiceContract Type의 메쏘드에 반드시 포함되어야 합니다. WebGetAttribute는 디스패처에게 해당 HTTP GET 요청에 응답해야 한다고 알려주고, WebInvokeAttribute는 디폴트로 HTTP POST에 매핑됩니다. 메쏘드 속성은 다른 HTTP 동사를 지원하기 위해 설정 가능합니다. (PUT 과 DELETE ). 디폴트로 URI는 메쏘드에 이름으로 결정됩니다.
UriTemplate 과 UriTemplateTable

WCF는 각 자원의 URI를 정의할 수 있습니다.
이 경우에 AddArticle 메쏘드에 Method="POST"를 가독성을 위해 추가했습니다. (디폴트: WebInvokeAttribute POST). GET 과 POST 메쏘드는 uriTemplate을 통해 URI 커스토마이제이션 가능합니다.

WebHttpBinding 와 WebHttpBehavior

WCF에서 바인딩이 WCF가 어떻게 통신할 것인지를 결정합니다. RESTful endpoint를 위해 WebHttpBinding을 사용합니다. 다른 바인딩과 다르게 WebHttpBinding는 굉장히 간단합니다. 두 개의 컴포넌트 (HTTP transport 와 text message encoder)

WebHttpBehavior는 URI와 동사 디스패처를 위해 사용됩니다. WebHttpBinding과 항상 함께 사용된다고 보면 되죠.

ServiceHost sh =
  new ServiceHost(typeof(MSDNMagazineServiceType));
string baseUri = "http://localhost/MagazineService";
ServiceEndpoint se =
  sh.AddServiceEndpoint(typeof(IMSDNMagazineService),
  new WebHttpBinding(), baseUri);
se.Behaviors.Add(new WebHttpBehavior());
sh.Open();
WebHttpBinding을 지정해야할 뿐 아니라, ServiceHost의 endpoint도 추가해야 합니다. 또한, 명시적으로 WebHttpBehavior를 추가하여 endpoint에 URI와 동사 디스패칭 시스템이 동작하도록 하는 작업이 추가되어야 합니다.
물론, 설정으로도 위와 동일한 작업을 할 수 있죠.
WebServiceHost 와 WebServiceHostFactory

WCF에 대한 불만 중 하나가 설정에 관해 때때로 너무 복잡하다는 것입니다. RESTful endpoint의 이러한 불만을 줄이기 위해, 마이크로소프트는 WebServiceHost 와 WebServiceHostFactory를 .NET Framework 3.5에 추가하였습니다.
WebServiceHost 는 RESTful endpoint의 자체 호스팅 시나리오를 간단히 하기 해 추가된 유형입니다.

string baseUri = "http://localhost/MagazineService";
WebServiceHost sh =
  new WebServiceHost(typeof(MSDNMagazineServiceType),
  new Uri(baseUri));
sh.Open();

바로 이 코드를 사용하면 WebHttpBinding, WebHttpBehavior를 수작업으로 매번 추가하는 번거로움을 덜 수 있습니다. WebServiceHost 클래스가 자동으로 endpoint을 만들고, WebHttpBinding, WebHttpBehavior가 하는 설정을 대신해주기 때문이죠. IIS 웹서버에서 WCF 매니지드 호스팅 시나리오에서 WCF는 보통 서비스 타입을 가리키기 위해 .svc 파일이필요하고, WCF에게 endpoint에 대한 정보를 알려주기 위해 web.config 파일에 entry 를 추가합니다.
즉, 매니지드 호스팅 시나리오를 단순화 하기 위해 마이크로소프트는 WebServiceHostFactory를 추가했는데, 여러 RESTful 서비스의 설정 관련 부분을 덜어주기 위해 확장 point를 개방합니다.

<%@ ServiceHost Factory=
  "System.ServiceModel.Activation.WebServiceHostFactory"  
  Service="MSDNMagazine.MSDNMagazineServiceType" %>
WebServiceHostFactory는  WebServiceHost의 인스턴스를 생성하고, WebServiceHost는 WebHttpBinding과 WebHttpBehavior를 이용해 endpoint를 자동설정 합니다. 물론 바인딩에 대한 정보가 변경될 때는 설정 파일에 적절한 entries 값을 추가해야 겠죠.

Figure 1 서비스 endpoint에 HTTP 기본 인증을 설정하는 파일
Figure 1 HTTP Basic Authentication

예제 코드 사용하기

서비스를 만들고 구동하고 있다면, 어떤 클라이언트를 통해서도 root URI를 입력할 수 있습니다. 브라우저를 이용한 RESTful endpoint 테스트를 위해 아래와 같이 한 번 해보시죠.

Figure 2

Figure 2 Root Resource URI
이 경우에 나는 Visual Studio 2008 웹 개발 서버에 해당 코드를 호스팅하고 있습니다. Issues.svc 파일은 WCF 매니지드 호스팅 시나리오에서 필요한 파일 입니다. 특정 년도 값을 보기 위해서는 아래와 같이 입력합니다. Figure 3
Figure 3 2007년도를 나타내는 자원
만약 2008년 10월을 요청하려고 한다면, URI는 localhost:1355/Issues.svc/2008/October가 될 것이다. (현재 10월 값이 없다고 하자) 추가하려면 HTTP POST로 해당 기사를 요청해보자.
 
 
Figure 4 기사 자원 생성
 
URI 자원을 알고, 원하는 동작을 안다면 서비스로 활용이 충분히 가능함을 느낄 수 있으시죠?
바로 이 REST 아키텍처 스타일을 활용하면 Web Feeds (RSS, Atom), JSON 등을 AJAX와 연동하여 활용가능함이 느껴지나요?

MSDN Jon Flanders의 글을 번역하였습니다
posted by Sunny's
2011. 5. 12. 13:54 ASP.NET


REST stands for Representational State Transfer. It is an architecture designed to represent resources via standard HTTP URIs and Verbs. REST provides a uniform interface for identifying resources, manipulating resources through representations, and including metadata that can make messages self-describing. REST is not tied to any platform or technology but is done on the Web using HTTP.


The following HTTP verbs may be used when creating RESTful services:

GET - Requests a specific representation of a resource
PUT - Create or update a resoure with the supplied representation
DELETE - Delete the specified resource
POST - Submit data to be processed by the identified resource
HEAD - Similar to GET but only retrieves headers
OPTIONS - Returns the methods supported by the identified resource

In this article I'll show how to build a "Notes" (a.k.a. "reminders") RESTful WCF service and consume it using jQuery / JSON from the client. We'll see how to create the service interface, what decorations (Attributes) are required, what's required in the web.config settings, how to make the jQuery $ajax calls to the service, and how to handle the server side logic with a small SQLite database. Everything in the downloadable solution is included - you need only unzip it and it should "work out of the box".

In order to create a WCF service, you can start with a standard ASP.NET Web Application project and add a new WCF Service.

To Create a complete WCF REST service, you start with the interface. Here's mine:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web.Script.Services;

namespace RESTFulNotes
{

    [ServiceContract]
    public interface IService1
    {
         //GetNotes: method to get all Notes. In this UriTemplate there is tag querystring item  ={tag} allows us to filter note by category name
        [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "Notes?tag={tag}")]
        [OperationContract]
        Notes FindNotes(string tag);

        [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "Notes/{ID}")]
        [OperationContract]
        Note GetNote(string ID);

        [WebInvoke(Method = "PUT", ResponseFormat = WebMessageFormat.Json, UriTemplate = "Notes/{ID}")]
        [OperationContract]
        string PutNote(Note Noteobj, string ID);

        [WebInvoke(Method = "DELETE", UriTemplate = "Notes/{ID}")]
        [OperationContract]
        void DeleteNote(string ID);
      }

    // Use a data contract as illustrated in the sample below to add composite types to service operations
    [DataContract]
    public class Note
    {
         [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Category { get; set; }
        [DataMember]
        public string Subject { get; set; }
        [DataMember]
        public string NoteText { get; set; }
    }

     [CollectionDataContract]
    public class Notes : List<Note>
    {
         public Notes() { }
        public Notes(List<Note> Notes) : base(Notes) { }
    }
}

Note that the WebInvoke decoration is the key item here. It defines the Method, the ResponseFormat, and the custom UriTemplate you want to use. With this information, the WCF runtime is able to handle everything - all you need to do is implement the methods in server-side code, and call them correctly from client script in your consumer page.

With the IService1 interface complete and all attributes correctly marked, we're now ready to implement our interface:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SQLite;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using System.Net;
using System.ServiceModel.Activation;
using System.Web;


namespace RESTFulNotes
{
  
    [AspNetCompatibilityRequirements(RequirementsMode =AspNetCompatibilityRequirementsMode.Allowed)]
    public class Service1 : IService1
    {
         private SQLiteConnection conn;
        Service1()
        {
            conn = Global.conn;
        }
      
         #region IService1 Members

        public Notes FindNotes(string tag)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
            Notes notes = new Notes();
            string Sql = "SELECT * FROM NOTE WHERE CATEGORY=@TAG";
            SQLiteCommand cmd = new SQLiteCommand(Sql, conn);
             cmd.Parameters.AddWithValue("@TAG", tag);
            SQLiteDataAdapter da = new SQLiteDataAdapter(Sql, conn);
            da.SelectCommand = cmd;
            DataTable dt = new DataTable();
             da.Fill(dt);
            Note note;
             if (dt.Rows.Count > 0)
            {
                foreach (DataRow row in dt.Rows)
                {

                    note = new Note();
                    note.ID = Convert.ToInt32(row["ID"]);
                    note.Category = (string) row["Category"];
                    note.Subject = (string) row["Subject"];
                    note.NoteText = (string) row["NoteText"];
                     notes.Add(note);
                 }
             }
             return notes;
        }

         public Note GetNote(string ID)
        {
            string Sql = "SELECT * FROM NOTE WHERE ID=@ID";
            SQLiteCommand cmd = new SQLiteCommand(Sql, conn);
             cmd.Parameters.AddWithValue("@ID", int.Parse(ID));
            SQLiteDataAdapter da = new SQLiteDataAdapter(Sql, conn);
            da.SelectCommand = cmd;
            DataTable dt = new DataTable();
             da.Fill(dt);
            Note note=null;
            if (dt.Rows.Count > 0)
            {
                DataRow row = dt.Rows[0];
                note = new Note();
                note.ID = Convert.ToInt32(row["ID"]);
                note.Category = (string)row["Category"];
                note.Subject = (string)row["Subject"];
                note.NoteText = (string)row["NoteText"];
            }
            
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
             return note;
        }

         public string PutNote(Note  note, string ID)
        {
            String Sql = "INSERT INTO NOTE (CATEGORY,SUBJECT,NOTETEXT) VALUES(@CATEGORY,@SUBJECT,@NOTETEXT)";
            SQLiteCommand cmd = new SQLiteCommand(Sql, conn);

             cmd.Parameters.AddWithValue("@CATEGORY", note.Category);
             cmd.Parameters.AddWithValue("@SUBJECT", note.Subject);
             cmd.Parameters.AddWithValue("@NOTETEXT", note.NoteText);
             if(cmd.Connection.State ==ConnectionState.Closed)
            cmd.Connection.Open();
            cmd.ExecuteNonQuery();
            cmd.CommandText = "SELECT MAX(ID) FROM NOTE";
            var id = cmd.ExecuteScalar();
            int retval = Convert.ToInt32(id);
             cmd.Connection.Close();
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Created;
            return "ID=" + retval.ToString();
        }

        public void DeleteNote(string ID)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Found;
          // delete the database row here
         }

         #endregion
    }
}


The actual SQLite connection is defined in global.asax.cs:

public static SQLiteConnection conn;
        void Application_Start(object sender, EventArgs e)
        {
            string cestring = "Data Source=" + HttpContext.Current.Server.MapPath("~/App_Data/Notes.db") + ";version=3";
            conn = new SQLiteConnection(cestring);
        }

This ensures that we get a path to the Notes.db SQLite database file no matter how we've deployed or unzipped the app.

The web.config requirements once the service methods are all implemented is as follows:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <authentication mode="Windows" />
    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="EndpointBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="ServiceBehavior" name="RESTFulNotes.Service1">
        <endpoint address="" binding="webHttpBinding" contract="RESTFulNotes.IService1" behaviorConfiguration="EndpointBehavior">      
        </endpoint>
      </service>
    </services>
  </system.serviceModel>  
</configuration>

Now let's switch over to the Default.aspx page, where all the action is in client script:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="RESTFulNotes._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
    <script src="Scripts/JSon.js" type="text/javascript"></script>
      <script type="text/javascript">
          var Type;
          var Url;
          var Data;
          var ContentType;
          var DataType;
          var ProcessData;
          var method;
          //Generic function to call ASMX/WCF  Service
          function CallService() {
               $.ajax({
                   type: Type, //GET,POST,PUT or DELETE verb
                  url: Url, // Location of the service
                  data: Data, //Data to be sent to server
                  contentType: ContentType, // content type sent to server
                  dataType: DataType, //Expected data format from server
                  processdata: ProcessData, //True or False
                  success: function (msg) {  //On Successful service call
                      ServiceSucceeded(msg);
                  },
                  error: ServiceFailed // function When Service call fails
              });
          }

          function ServiceFailed(result) {
               alert('Service call failed: ' + result.status + '' + result.statusText);
             Type = null; Url = null; Data = null; ContentType = null; DataType = null; ProcessData = null;
          }

          function GetNote() {
              var ID = "5";
             Type = "POST";
              Url = "Service1.svc/Notes/" + ID;
               //Data = '{"ID": "' + ID + '"}';
              ContentType = "application/json; charset=utf-8";
              DataType = "json";
              ProcessData = false;
              method = "GetNote";
              CallService();
          }
          
          function FindNotes() {
             Type = "POST";
              var tag = "Chores";
              Url = "Service1.svc/Notes?tag=" + tag;
              Data = '{"tag": "' + tag + '"}';
              ContentType = "application/json; charset=utf-8";
              DataType = "json";
              ProcessData = true;
              method = "FindNotes";
              CallService();
          }

          function CreateNote() {
             Type = "PUT";
              Url = "Service1.svc/Notes/0";
              var msg2 = {  "Category" : "Chores", "Subject": "To Remember", "NoteText": "Get Milk!" };
              Data = JSON.stringify(msg2);
              ContentType = "application/json; charset=utf-8";
              DataType = "json";
              ProcessData = true;
              method = "CreateNote";
              CallService();
          }


          function ServiceSucceeded(result) {
               if (DataType == "json") {

                   if (method == "CreateNote") {
                       document.getElementById("display").innerHTML = "CREATED: " + result;
                   }
                   else if (method == "GetNote") {
                  resultObject = result.GetNoteResult;
                      var string = " ID:" + result.ID + "<br/>" + "CATEGORY: " + result.Category + "<br/>" + "SUBJECT: " + result.Subject + "<br/>NOTE: " + result.NoteText;
                       document.getElementById("display").innerHTML = string;
                   }

                    else if (method == "FindNotes") {
                    var string="";
                      resultObject = result;

                       for( result in resultObject){
                          string += " ID:" + resultObject[result].ID + "<br/>" + "CATEGORY: " + resultObject[result].Category + "<br/>" + "SUBJECT: " + resultObject[result].Subject + "<br/>NOTE: " + resultObject[result].NoteText + "<Br/>";
                       }
                        document.getElementById("display").innerHTML = string;
                  }

              }
          }

          function ServiceFailed(xhr) {
               alert("FAIL" +xhr.responseText);
               if (xhr.responseText) {
                  var err = xhr.responseText;
                   if (err)
                       error(err);
                   else
                      error({ Message: "Unknown server error." })
               }
               return;
          }

          $(document).ready(
         function () {
             //   CreateNote();
             // GetNote();
             FindNotes();
         }
         );
        
    </script>
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <div id="display">    
    </div>
</asp:Content>

You can see above that I have two script tags defined, one to the google jQuery CDN, and the other to Crockford's Json.js hosted locally.

The business logic in the last script section includes a "genericized" jQuery $ajax call:

           var Type;
          var Url;
          var Data;
          var ContentType;
          var DataType;
          var ProcessData;
          var method;
          //Generic function to call ASMX/WCF  Service
          function CallService() {
               $.ajax({
                   type: Type, //GET,POST,PUT or DELETE verb
                  url: Url, // Location of the service
                  data: Data, //Data to be sent to server
                  contentType: ContentType, // content type sent to server
                  dataType: DataType, //Expected data format from server
                  processdata: ProcessData, //True or False
                  success: function (msg) {  //On Successful service call
                      ServiceSucceeded(msg);
                  },
                  error: ServiceFailed // function When Service call fails
              });
          }

The above allows us to set parameters and make service calls for any method.

I have a ServiceSucceeded function that handles display of results for various method calls, and a ServiceFailed function to show any error messages.

$(document).ready {... } is where you can experiment with whatever function you want to try.

The other functions are just "set ups" for the CallService method. I did not create a fancy jQuery UI for this as it is outside the scope of the article. Ambitious programmers will want to use the jQuery Templating mechanism to populate grids, etc with results.

You can download the fully  functional Visual Studio 2010 Solution which includes the standalone System.Data.Sqlite assembly (this is a mixed - mode assembly which has both the ADO.NET provider and the actual C++ database engine), as well as the sample Notes.db database.



출처 : http://www.eggheadcafe.com/tutorials/aspnet/b480ba4e-b59c-43d4-ac4b-2990ca19daec/restful-aspnet-wcf--jquery--json-service-with-get-post-put-and-delete.aspx


posted by Sunny's
2011. 5. 12. 09:38 ASP.NET

RESTful 은 ROA를 따르는 웹서비스 디자인 표준이다.

웹의 모든 리소스를 URI로 표현하고 이를 구조적으고 유기적으로 연결하여
비상태 지향적인 방법으로 일관된 메소드를 사용하여 리소스를 하용하는
웹서비스의 디자인 표준..

여기로 가보면 정리를 잘해 놓으셨네요..
http://www.iamcorean.net/22


WCF를 활용한 RESTful 서비스에 대한 소개 (WOA) : http://www.cooolguy.net/153


 

posted by Sunny's
2010. 3. 5. 18:01 ASP.NET

REST 아키텍처 스타일에 대해서는 앞의 글에서도 소개했었는데, 오늘은 WCF 서비스를 활용한 방법에 대해 이야기하려고 합니다. REST(Representational State Transfer)에 대해서는 많은 분들이 도대체 뭐야? 라는 반응을 보이십니다.
하지만, 앞으로 정말 많이, 여러번 듣게 되실 개념이기 때문에 꼭 이해가 필요합니다.

WOA(Web Oriented Architecture)가 결국은 RESTful 아키텍처를 의미합니다.

REST란?
아키텍처 스타일이란 말은 어떤 무엇인가를 구축할 때 적용되는 규약들의 집합이라고 이야기할 수 있습니다. 즉, 소프트웨어 시스템을 구현할 때 사용되는 가이드 정도로 이해하면 되겠습니다.

클라이언트가 서비스(Endpoint)를 요청할 때 사용되는 클라이언트 – 서버 아키텍처 스타일 중의 하나입니다.
HTTP 스펙을 만든 사람 중의 한 명인 Roy Fielding 교수가 박사학위 논문에서 사용한 개념 입니다.
Architectural Styles and the Design of Network-based Software Architectures

Web이 바로 REST 아키텍처 스타일을 그대로 따르고 있는 시스템 입니다. 원칙을 한 번 살펴보도록 하죠.

  • 사용자 에이전트가 자원과 상호작용하는데, 자원은 URI로 표현될 수 있는 모든 것들이 대상입니다.
  • 자원과의 상호작용은 HTTP의 표준 동사(GET, POST, PUT, and DELETE)를 사용하여 이루어집니다. 또한, 상호작용을 위해 자원의 미디어타입이 결정되는데 HTTP 컨텐츠 타입의 헤더를 사용합니다.
    (XHTML, XML, JPG, PNG, and JSON are some well-known media types.)
  • 자원은 필요한 모든 정보를 다 포함하고 있습니다. (서비스가 stateless 형태로 존재)
  • 자원은 다른 자원과의 링크를 포함합니다. (하이퍼미디어)

하나의 간단한 예제로 이해를 해보도록 하죠.

MSDN 매거진의 데이터을 처리하는 서비스를 만든다고 가정해보죠.
- 해당 서비스는 지금까지 발행된 모든 MSDN 매거진에 대한 정보를 알려줍니다.
  매거진의 편집자가 이 서비스를 쓸 것이고 새로 발행되는 매거진에 대한 정보를 추가하고, 관리하는 등의 작업을 할
   예정 입니다.

RESTful 서비스를 구축할 때 서비스 디자인 단계를 살펴보죠

  1. 자원은 어떤 것들이 될 것인지?
  2. 자원을 표현하는데 사용할 URI로 어떤 값을 쓸 것인지?
  3. 단일 인터페이스 (HTTP 동사) 중 어떤 것을 쓸 것인지?

RESTful endpoint를 만드는 기본적인 블럭들입니다. 자원, 그리고 표현 방법

자원: MSDN 잡지가 출간된 모든 년도, 매년 발행된 이슈, 매거진에 게재된 기사
해당 자원을 표현하기 위해 XML을 사용하기로 하죠, 하지만 RESTful서비스는 XML에 한정된 것은 아니고 여러 유형으로 표현 가능합니다.

이제 URI를 결정해보죠. 절대경로는 이 endpoint를 어떤 호스트에 배치할 것인지에 달라지기 때문에, 여기서는 상대경로로 표현하도록 하겠습니다.
모든 년도들의 리스트, 즉 서비스의 루트 URI로 / 를 사용합니다.
이 구문을 활용하면, /{year} 는 해당 년도에 발행된 모든 잡지들에 대한 정보를 리턴합니다.
/{year}/{issue}는 해당 년도에 발행된 특정한 이슈에 대한 값을 리턴하겠죠.
/{year}/{issue}/{article}은 해당 이슈의 기사에 대한 정보값을 리턴하는데, 각 기사는 1 .. n까지 번호가 매겨졌다고 가정하죠

자, 이제 해당 URI를 단일인터페이스 (HTTP 동사)와 연결해보죠. 해당 잡지의 히스토리는 읽기 전용이어야 하므로, 해당 root 자원은 GET 동사만 사용되어야 합니다. 새로운 년도는 추가될 수 있으므로 PUT을 통해 /{year}가 추가되고, PUT을 통해 수정도 이루어질 수 있습니다. POST 연산을 통해서는 해당 자원을 신규로 생성할 때 사용되는데 해당 이슈를 만드는, /{year}/{issue} 등으로 사용합니다.

예를들어, 2006년 1월 이슈의 모든 기사를 원한다면 HTTP GET /2006/January 라고 가져올 수 있을 것이고 2009년 3월 이슈를 만들려면 POST /2008/december라고 쓸 수 있다는 것이죠.

그렇다면 왜 REST에 대해 이야기를 할까요?
서비스를 사용하는 방법이 REST 밖에 없을까요? 그렇지 않습니다.

클라이언트 서버 스타링의 애플리케이션을 구현하는데 있어서 다른 아키텍처 스타일을 사용할 수 있습니다. .NET의 특화된 RPC(Remote Procedure Call), 즉 DCOM 및 .NET 리모팅 등을 사용하거나 상호운용성이 제공되는 ASMX를 사용하는 SOAP, WCF 등의 RPC 기술을 사용할 수 있었죠.
그런데, REST를 사용하면 첫째, 훨씬 간편하고 쉽게 서비스를 구현할 수 있고 둘째, 마이크로소프트가 RPC 기술(SOAP 포함)에 비해 REST 아키텍처 스타일을 훨씬 더 선호하고 있기 때문인데, 그 이유가 있다는 거죠. 마이크로소프트의 클라우드 컴퓨팅, 애저 서비스 플랫폼과 윈도 애저에서도 REST 스타일을 채택하고 있습니다.

장점
Caching HTTP 동사를 통해 RESTfu endpoint가 불리워 질 때, HTTP 동사 GET을 사용합니다. GET으로 불리워진 자원들은 여러 다양한 방법으로 캐싱이 가능합니다. 결국 속도와 확장성이 향상됩니다.

Scale-Out REST는 각 자원이 특정 요청에 필요한 모든 상태 값을 다 가지고 있기 때문에, 즉 stateless기 때문에 확장이 훨씬 용이합니다.

Idempotent GET을 이용해 자원을 요청할 때 추가적인 부작용이 벌어지지 않습니다 (즉, 다시 한 번 GET, DELETE 등을 불러도 두번 중복작업이 이루어지지 않음)

Interoperability SOAP이 클라이언트 서버 프로그램을 통해 상호운용성이 뛰어나다고 하지만, 몇 몇 언어 및 환경은 여전히 SOAP 툴킷을 갖고 있지 못하고, 툴킷을 가지고 있는 언어의 경우에도 최신 표중르 가진 다른 툴킷과 통신할 수 없는 경우가 종종 발생합니다. REST는 HTTP 동사만을 사용하기 때문에 어떤 언어, 플랫폼과도 상호운용이 가능합니다.

Simplicity 너무 단순하게 구현이 가능합니다. URI와 HTTP 동사는 모든 웹개발자들이 다 알고 있는 내용입니다.

가능한 경우에 REST를 많이 사용하시기 바라고, 엔터프라이즈에서도 고려하고 있는 중입니다.

WCF and REST

WCF는 스타일, 프로토콜에 무관하게 통신이 가능한 분산 컴퓨팅 환경의 애플리케이션을 구축하는데 사용되는 마이크로소프트의 프레임웍 입니다. WCF 개념은 개발자가 한 프로그래밍 언어를 통해 다른 많은 분산 시스템을 사용 가능하도록 돕는 기술 입니다.

이전 버전의 WCF는 RPC(SOAP 등)를 이용하도록 사용되었는데, .NET Framwork 3.0부터는 REST 서비스를 사용할 수 있도록 지원하고 있었는데 REST 기반 서비스 프로그래밍이 가능한 모델 및 인프라 지원이 부족했는데, .NET Framwork 3.5 SP1에 프로그래밍 모델 및 기타 많은 부분이 개선되었습니다. 저도 조금 살펴봤는데 잘 만들어졌습니다.

프로그래밍 모델 중 두가지 속성, WebGetAttribute 과 WebInvokeAttribute, URI template 메커니즘이 주목할 만합니다.
URI, 메쏘드에 적용될 동사를 정의할 수 있도록 해주는 거죠. 인프라 영역에서는 WebHttpBinding 바인딩과 REST가 적절한 네트웍 스택을 사용할 수 있도록 WebHttp­Behavior가 지원됩니다. 또한, 커스톰 Service­Host (WebServiceHost) 와 ServiceHostFactory (WebServiceHostFactory)가 포함되어 있습니다.

WebGetAttribute 와 WebInvokeAttribute

WCF는 연결된 시스템 구축을 간단하게 해주는데, 네트웍 메시지를 여러분이 서비스를 구현하기 위해 정의한 클래스의 인스턴스로 라우팅해버린다. 결국, 네트웍 트랙픽을 처리하기 위한 인프라에 신경쓰지 않고, 코드의 로직에 집중할 수 있게 해주는 거죠. WCF와 인프라를 함께 사용할 때 기본 디스패처는 요청에서 사용하는 URI를 기준으로 라우팅을 진행합니다. 즉, 이 라우팅 방식을 사용하면 RESTful endpoint를 아주 쉽게 구현할 수 있고, 실제로 WebHttpDispatchOperationSelector를 사용합니다. 

이것이 가능하도록 하는 핵심은 WebHttpDispatch­OperationSelector는 서로 다른 URI와 동사를 여러분이 만든 메쏘드에 어떻게 매핑할 것인지를 아는 거죠. WebGetAttribute 와 WebInvokeAttribute는 WCF ServiceContract Type의 메쏘드에 반드시 포함되어야 합니다. WebGetAttribute는 디스패처에게 해당 HTTP GET 요청에 응답해야 한다고 알려주고, WebInvokeAttribute는 디폴트로 HTTP POST에 매핑됩니다. 메쏘드 속성은 다른 HTTP 동사를 지원하기 위해 설정 가능합니다. (PUT 과 DELETE ). 디폴트로 URI는 메쏘드에 이름으로 결정됩니다.
UriTemplate 과 UriTemplateTable

WCF는 각 자원의 URI를 정의할 수 있습니다.
이 경우에 AddArticle 메쏘드에 Method="POST"를 가독성을 위해 추가했습니다. (디폴트: WebInvokeAttribute POST). GET 과 POST 메쏘드는 uriTemplate을 통해 URI 커스토마이제이션 가능합니다.

WebHttpBinding 와 WebHttpBehavior

WCF에서 바인딩이 WCF가 어떻게 통신할 것인지를 결정합니다. RESTful endpoint를 위해 WebHttpBinding을 사용합니다. 다른 바인딩과 다르게 WebHttpBinding는 굉장히 간단합니다. 두 개의 컴포넌트 (HTTP transport 와 text message encoder)

WebHttpBehavior는 URI와 동사 디스패처를 위해 사용됩니다. WebHttpBinding과 항상 함께 사용된다고 보면 되죠.

ServiceHost sh =
  new ServiceHost(typeof(MSDNMagazineServiceType));
string baseUri = "http://localhost/MagazineService";
ServiceEndpoint se =
  sh.AddServiceEndpoint(typeof(IMSDNMagazineService),
  new WebHttpBinding(), baseUri);
se.Behaviors.Add(new WebHttpBehavior());
sh.Open();
WebHttpBinding을 지정해야할 뿐 아니라, ServiceHost의 endpoint도 추가해야 합니다. 또한, 명시적으로 WebHttpBehavior를 추가하여 endpoint에 URI와 동사 디스패칭 시스템이 동작하도록 하는 작업이 추가되어야 합니다.
물론, 설정으로도 위와 동일한 작업을 할 수 있죠.
WebServiceHost 와 WebServiceHostFactory

WCF에 대한 불만 중 하나가 설정에 관해 때때로 너무 복잡하다는 것입니다. RESTful endpoint의 이러한 불만을 줄이기 위해, 마이크로소프트는 WebServiceHost 와 WebServiceHostFactory를 .NET Framework 3.5에 추가하였습니다.
WebServiceHost 는 RESTful endpoint의 자체 호스팅 시나리오를 간단히 하기 해 추가된 유형입니다.

string baseUri = "http://localhost/MagazineService";
WebServiceHost sh =
  new WebServiceHost(typeof(MSDNMagazineServiceType),
  new Uri(baseUri));
sh.Open();

바로 이 코드를 사용하면 WebHttpBinding, WebHttpBehavior를 수작업으로 매번 추가하는 번거로움을 덜 수 있습니다. WebServiceHost 클래스가 자동으로 endpoint을 만들고, WebHttpBinding, WebHttpBehavior가 하는 설정을 대신해주기 때문이죠. IIS 웹서버에서 WCF 매니지드 호스팅 시나리오에서 WCF는 보통 서비스 타입을 가리키기 위해 .svc 파일이필요하고, WCF에게 endpoint에 대한 정보를 알려주기 위해 web.config 파일에 entry 를 추가합니다.
즉, 매니지드 호스팅 시나리오를 단순화 하기 위해 마이크로소프트는 WebServiceHostFactory를 추가했는데, 여러 RESTful 서비스의 설정 관련 부분을 덜어주기 위해 확장 point를 개방합니다.

<%@ ServiceHost Factory=
  "System.ServiceModel.Activation.WebServiceHostFactory"  
  Service="MSDNMagazine.MSDNMagazineServiceType" %>
WebServiceHostFactory는  WebServiceHost의 인스턴스를 생성하고, WebServiceHost는 WebHttpBinding과 WebHttpBehavior를 이용해 endpoint를 자동설정 합니다. 물론 바인딩에 대한 정보가 변경될 때는 설정 파일에 적절한 entries 값을 추가해야 겠죠.

Figure 1 서비스 endpoint에 HTTP 기본 인증을 설정하는 파일
Figure 1 HTTP Basic Authentication

예제 코드 사용하기

서비스를 만들고 구동하고 있다면, 어떤 클라이언트를 통해서도 root URI를 입력할 수 있습니다. 브라우저를 이용한 RESTful endpoint 테스트를 위해 아래와 같이 한 번 해보시죠.

Figure 2

Figure 2 Root Resource URI
이 경우에 나는 Visual Studio 2008 웹 개발 서버에 해당 코드를 호스팅하고 있습니다. Issues.svc 파일은 WCF 매니지드 호스팅 시나리오에서 필요한 파일 입니다. 특정 년도 값을 보기 위해서는 아래와 같이 입력합니다. Figure 3
Figure 3 2007년도를 나타내는 자원
만약 2008년 10월을 요청하려고 한다면, URI는 localhost:1355/Issues.svc/2008/October가 될 것이다. (현재 10월 값이 없다고 하자) 추가하려면 HTTP POST로 해당 기사를 요청해보자.
 
 
Figure 4 기사 자원 생성
 
URI 자원을 알고, 원하는 동작을 안다면 서비스로 활용이 충분히 가능함을 느낄 수 있으시죠?
바로 이 REST 아키텍처 스타일을 활용하면 Web Feeds (RSS, Atom), JSON 등을 AJAX와 연동하여 활용가능함이 느껴지나요?

MSDN Jon Flanders의 글을 번역하였습니다.
posted by Sunny's
prev 1 next