#서론
간만에 python과 TurboGears의 정리 포스트다. 대략 2달전쯤 끄적여놓은 건데, 이제서야 정리하게 되었다.
TurboGears의 TGWebService 라이브러리는 간단하게 WebService 메소드와 WSDL을 생성해준다.
ZSI는 TGWebService가 생성한 WSDL로 WebService Client를 위한 클래스들을 생성해준다.

이번 포스트에서는 TGWebService와 ZSI를 이용해서 간단한 WebService Server/Client를 생성해본다.
필요한 라이브러리는 다 설치되어 있다는 가정하에 진행하도록 한다.


#필요한 Python Library


#TurboGears의 새 프로젝트 생성하기
tg-admin quickstart CodeGeass


#WebService로 제공할 클래스와 메소드를 정의하기
from tgwebservices.controllers import WebServicesRoot, \
    WebServicesController, wsexpose, wsvalidate

from tgwebservices.runtime import typedproperty, unsigned
class Person(object):        
    name = ''
    id = ''

class CodeGeass(WebServicesRoot):
    @wsexpose(Person)
    @wsvalidate(Person)
    def getPerson(self, value):
        person = Person()
        person.id = value.name
        person.name = value.id

        return person

- @wsexpose는 getPerson이 반환하는 class의 type이다.
- @wsvalidate는 getPerson이 호출될때의 parameter type이다.
- type에는 built-in type(int, str 등)과 user-define class(단 object 상속해야한다)들이 포함될 수 있다.


#TurboGears의 root controller에 TGWebService 연결하기(controller.py에 하위 클래스 추가)
from turbogears import controllers, expose, flash
from codegeass import CodeGeass
# from model import *        
# import logging             
# log = logging.getLogger("codegeass.controllers")

class Root(controllers.RootController):
    codegeass = CodeGeass("http://localhost:8080/codegeass/")

    @expose(template="codegeass.templates.welcome")
    def index(self):
        import time          
        # log.debug("Happy TurboGears Controller Responding For Duty")
        flash("Your application is now running")
        return dict(now=time.ctime())

- codegeass = .... 이 부분이 앞에서 만들어놓은 CodeGeass class를 root controller와 연결하는 구문이다. 생성자의 parameter는  CodeGeass의 instance가  web상에서 호출되는 URI를 다 넣어준다. (http://localhost:8080/codegeass/ 에서 codegeass/가 해당된다. 이해 잘 안된다면 TurboGears의 controller에 대해 구글링해서 공부하는 것이 좋다)


#TurboGears 서버 구동하기
./start-codegeass.py


#TGWebService로 생성된 WSDL 확인하기
http://localhost:8080/codegeass/soap/api.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CodeGeass" xmlns:types="http://localhost:8080/codegeass/soap/types" xmlns:soapenc="http://www.w3.org/2001/09/soap-encoding" targetNamespace="http://localhost:8080/codegeass/soap/" xmlns:tns="http://localhost:8080/codegeass/soap/">
   <wsdl:types>
     <xsd:schema elementFormDefault="qualified" targetNamespace="http://localhost:8080/codegeass/soap/types">
        <xsd:complexType name="Person">
          <xsd:sequence>
            <xsd:element name="id" type="xsd:string"/><xsd:element name="name" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
          <xsd:element name="getPerson">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="types:Person"/>
              </xsd:sequence>
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="getPersonResponse">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="result" type="types:Person"/>
              </xsd:sequence>
            </xsd:complexType>
          </xsd:element>
      </xsd:schema>
   </wsdl:types>

   <wsdl:message name="getPersonRequest" xmlns="http://localhost:8080/codegeass/soap/types">
       <wsdl:part name="parameters" element="types:getPerson"/>
   </wsdl:message>

   <wsdl:message name="getPersonResponse" xmlns="http://localhost:8080/codegeass/soap/types">
      <wsdl:part name="parameters" element="types:getPersonResponse"/>
   </wsdl:message>
   <wsdl:portType name="CodeGeass_PortType">
      <wsdl:operation name="getPerson">
         <wsdl:input message="tns:getPersonRequest"/>
         <wsdl:output message="tns:getPersonResponse"/>
      </wsdl:operation>
   </wsdl:portType>

   <wsdl:binding name="CodeGeass_Binding" type="tns:CodeGeass_PortType">
      <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="getPerson">
         <soap:operation soapAction="getPerson"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>

   <wsdl:service name="CodeGeass">
      <wsdl:documentation>WSDL File for CodeGeass</wsdl:documentation>
      <wsdl:port binding="tns:CodeGeass_Binding" name="CodeGeass_PortType">
         <soap:address location="http://localhost:8080/codegeass/soap/"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

- xml로 정의된 Person, 그리고 getPerson을 볼 수 있다.
- 몇개의 URL이 더 있는데, TGWebServices의 README.txt의 URLs를 참고하면 된다.


#ZSI로 WebService client를 위한 코드 생성하기
wsdl2py --url http://localhost:8080/codegeass/soap/api.wsdl

-WSDL을 이용해서 코드를 생성한다.(Code Generation from WSDL and XML Schema 참고) 위의 명령을 실행하면 다음과 같은 python 파일이 생성된다.
CodeGeass_services.py : TGWebServices에서의 portType, Locator 등의 Class가 있는 파일
CodeGeass_services_types.py : TGWebServices에서 정의된 Data Type이 정의된 Class가 있는 파일  


#생성된 코드를 이용하여 WebService Client 만들기
from CodeGeass_services import *
from CodeGeass_services_types import ns0

loc = CodeGeassLocator()
port = loc.getCodeGeass_PortType()
req = getPersonRequest()

inputMessage = ns0.Person_Def(None, None)
inputMessage._id = "dada"
inputMessage._name = "lulush"

req._value = inputMessage
res = port.getPerson(req)

returnMessage = res._result
print "Result ID:", returnMessage._id
print "Result name:", returnMessage._name

Locator와 Port를 설정하고, 호출할 메소드의 Request(getPerson+Request)에 파라메터를 넣는다.
그리고 Port를 통해 호출하면 값을 받아올 수 있다.
req._value 와 res._result는 위의 WSDL의 <xsd:element name="getPerson">, <xsd:element name="getPersonResponse">에서 하위 노드의 value 값과 매칭된다.


#실행

위의 소스 코드를 실행하면, inputMessage의 _id와 _name이 swap되어 나오면 정상적으로 처리된 것이다.


#결론
TGWebService를 사용해서 간단히 WSDL을 만들고, 그 WSDL을 ZSI로 코드 생성하고 클라이언트 만드는 방법을 알아보았다. 여기서는 TGWebService의 WSDL을 사용했는데, 조금 응용하면 자바, php등의 WSDL을 가지고 Client를 만들 수 있으며, 역으로 다른 시스템에서 TGWebService의 WSDL로 Client를 생성할 수도 있다.
다음 포스팅은 Java의 XFire와 TGWebService의 연동 방법에 대해 알아본다.
Posted by xHuro
,