Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
ͨ¹ýdemoѧϰOpenStack¿ª·¢--API·þÎñ£¨Ï£©
 
×÷ÕߣºÎºÐÇ À´Ô´£ºÁõ³Âãü ·¢²¼ÓÚ 2016-2-17
  3619  次浏览      28
 

ÉÏһƪÎÄÕÂÎÒÃÇÁ˽âÁËÒ»¸ö¾Þ†ªàµĿò¼Ü£ºPaste + PasteDeploy + Routes + WebOb¡£ºóÀ´£¬OpenStackÉçÇøµÄÈËÊܲ»ÁËÕâô†ªàµĴúÂëÁË£¬¾ö¶¨»»Ò»¸ö¿ò¼Ü£¬ËûÃÇ×îÖÕÑ¡ÖÐÁËPecan¡£Pecan¿ò¼ÜÏà±ÈÉÏһƪÎÄÕµĆªà¿ò¼ÜÓÐÈçϺô¦£º

1.²»ÓÃ×Ô¼ºÐ´WSGI applicationÁË

2.ÇëÇó·ÓɺÜÈÝÒ׾ͿÉÒÔʵÏÖÁË

×ܵÄÀ´Ëµ£¬ÓÃÉÏPecan¿ò¼ÜÒԺ󣬺ܶàÖØ¸´µÄ´úÂë²»ÓÃдÁË£¬¿ª·¢ÈËÔ±¿ÉÒÔרעÓÚÒµÎñ£¬Ò²¾ÍÊÇʵÏÖÿ¸öAPIµÄ¹¦ÄÜ¡£

Pecan

Pecan¿ò¼ÜµÄÄ¿±êÊÇʵÏÖÒ»¸ö²ÉÓöÔÏó·Ö·¢·½Ê½½øÐÐURL·ÓɵÄÇáÁ¿¼¶Web¿ò¼Ü¡£Ëü·Ç³£×¨×¢ÓÚ×Ô¼ºµÄÄ¿±ê£¬ËüµÄ´ó²¿·Ö¹¦Äܶ¼ºÍURL·ÓÉÒÔ¼°ÇëÇóºÍÏìÓ¦µÄ´¦ÀíÏà¹Ø£¬¶ø²»È¥ÊµÏÖÄ£°å¡¢°²È«ÒÔ¼°Êý¾Ý¿â²ã£¬ÕâЩ¶«Î÷¶¼¿ÉÒÔͨ¹ýÆäËûµÄ¿âÀ´ÊµÏÖ¡£¹ØÓÚPecanµÄ¸ü¶àÐÅÏ¢£¬¿ÉÒԲ鿴Îĵµ¡£±¾ÎÄÒÔ£¬OpenStackµÄmagnumÏîĿΪÀýÀ´ËµÃ÷PecanÏîÄ¿ÔÚʵ¼ÊÖеÄÓ¦Ó㬵«ÊDZ¾ÎIJ»»áÏêϸ½²½âPecanµÄ¸÷¸ö·½Ã棬һЩϸ½ÚÇë¶ÁÕßÔĶÁPecanµÄÎĵµ¡£

ÏîÄ¿ÖеĴúÂë½á¹¹

ʹÓÃPecan¿ò¼Üʱ£¬£¬OpenStackÏîĿһ°ã»á°ÑAPI·þÎñµÄʵÏÖ¶¼·ÅÔÚÒ»¸öapiĿ¼Ï£¬±ÈÈçmagnumÏîÄ¿ÊÇÕâÑùµÄ£º

? ~/openstack/env/p/magnum git:(master) $ tree magnum/api
magnum/api
©À©¤©¤ app.py
©À©¤©¤ auth.py
©À©¤©¤ config.py
©À©¤©¤ controllers
©¦ ©À©¤©¤ base.py
©¦ ©À©¤©¤ __init__.py
©¦ ©À©¤©¤ link.py
©¦ ©À©¤©¤ root.py
©¦ ©¸©¤©¤ v1
©¦ ©À©¤©¤ base.py
©¦ ©À©¤©¤ baymodel.py
©¦ ©À©¤©¤ bay.py
©¦ ©À©¤©¤ certificate.py
©¦ ©À©¤©¤ collection.py
©¦ ©À©¤©¤ container.py
©¦ ©À©¤©¤ __init__.py
©¦ ©À©¤©¤ magnum_services.py
©¦ ©À©¤©¤ node.py
©¦ ©À©¤©¤ pod.py
©¦ ©À©¤©¤ replicationcontroller.py
©¦ ©À©¤©¤ service.py
©¦ ©À©¤©¤ types.py
©¦ ©À©¤©¤ utils.py
©¦ ©¸©¤©¤ x509keypair.py
©À©¤©¤ expose.py
©À©¤©¤ hooks.py
©À©¤©¤ __init__.py
©À©¤©¤ middleware
©¦ ©À©¤©¤ auth_token.py
©¦ ©À©¤©¤ __init__.py
©¦ ©¸©¤©¤ parsable_error.py
©À©¤©¤ servicegroup.py
©¸©¤©¤ validation.py

ÄãÒ²¿ÉÒÔÔÚCeilometerÏîÄ¿Öп´µ½ÀàËÆµÄ½á¹¹¡£½éÉÜһϼ¸¸öÖ÷ÒªµÄÎļþ£¬ÕâÑùÄãÒÔºó¿´µ½Ò»¸öʹÓÃPecanµÄ£¬OpenStackÏîĿʱ¾Í»á±È½ÏÈÝÒ×ÕÒµ½Èë¿Ú¡£

app.py Ò»°ã°üº¬ÁËPecanÓ¦ÓõÄÈë¿Ú£¬°üº¬Ó¦Óóõʼ»¯´úÂë

config.py °üº¬PecanµÄÓ¦ÓÃÅäÖ㬻ᱻapp.pyʹÓÃ

controllers/ Õâ¸öĿ¼»á°üº¬ËùÓеĿØÖÆÆ÷£¬Ò²¾ÍÊÇAPI¾ßÌåÂß¼­µÄµØ·½

controllers/root.py Õâ¸ö°üº¬¸ù·¾¶¶ÔÓ¦µÄ¿ØÖÆÆ÷

controllers/v1/ Õâ¸öĿ¼¶ÔÓ¦v1°æ±¾µÄAPIµÄ¿ØÖÆÆ÷¡£Èç¹ûÓжà¸ö°æ±¾µÄAPI£¬ÄãÒ»°ãÄÜ¿´µ½v2µÈĿ¼¡£

´úÂë±äÉÙÁË£ºapplicationµÄÅäÖÃ

PecanµÄÅäÖúÜÈÝÒ×£¬Í¨¹ýÒ»¸öPythonÔ´ÂëʽµÄÅäÖÃÎļþ¾Í¿ÉÒÔÍê³É»ù±¾µÄÅäÖá£Õâ¸öÅäÖõÄÖ÷ҪĿµÄÊÇÖ¸¶¨Ó¦ÓóÌÐòµÄroot£¬È»ºóÓÃÓÚÉú³ÉWSGI application¡£ÎÒÃÇÀ´¿´MagnumÏîÄ¿µÄÀý×Ó¡£MagnumÏîÄ¿ÓиöAPI·þÎñÊÇÓÃPecanʵÏֵģ¬ÔÚmagnum/api/config.pyÎļþÖпÉÒÔÕÒµ½Õâ¸öÎļþ£¬Ö÷ÒªÄÚÈÝÈçÏ£º

app = {
'root': 'magnum.api.controllers.root.RootController',
'modules': ['magnum.api'],
'debug': False,
'hooks': [
hooks.ContextHook(),
hooks.RPCHook(),
hooks.NoExceptionTracebackHook(),
],
'acl_public_routes': [
'/'
],
}

ÉÏÃæÕâ¸öapp¶ÔÏó¾ÍÊÇPecanµÄÅäÖã¬Ã¿¸öPecanÓ¦Óö¼ÐèÒªÓÐÕâôһ¸öÃûΪappµÄÅäÖá£appÅäÖÃÖÐ×îÖ÷ÒªµÄ¾ÍÊÇrootµÄÖµ£¬Õâ¸öÖµ±íʾÁËÓ¦ÓóÌÐòµÄÈë¿Ú£¬Ò²¾ÍÊÇ´ÓÄĸöµØ·½¿ªÊ¼½âÎöHTTPµÄ¸ùpath£º/¡£hooks¶ÔÓ¦µÄÅäÖÃÊÇһЩPecanµÄhook£¬×÷ÓÃÀàËÆÓÚWSGI Middleware¡£

ÓÐÁËappÅäÖú󣬾ͿÉÒÔÈÃPecanÉú³ÉÒ»¸öWSGI application¡£ÔÚMagnumÏîÄ¿ÖУ¬magnum/api/app.pyÎļþ¾ÍÊÇÉú³ÉWSGI applicationµÄµØ·½£¬ÎÒÃÇÀ´¿´Ò»ÏÂÕâ¸öµÄÖ÷ÒªÄÚÈÝ£º

def get_pecan_config():
# Set up the pecan configuration
filename = api_config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename)


def setup_app(config=None):
if not config:
config = get_pecan_config()

app_conf = dict(config.app)

app = pecan.make_app(
app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
wrap_app=middleware.ParsableErrorMiddleware,
**app_conf
)

return auth.install(app, CONF, config.app.acl_public_routes)

get_pecan_config()·½·¨¶ÁÈ¡ÎÒÃÇÉÏÃæÌáµ½µÄconfig.pyÎļþ£¬È»ºó·µ»ØÒ»¸öpecan.configuration.Config¶ÔÏó¡£setup_app()º¯ÊýÊ×Ïȵ÷ÓÃget_pecan_config()º¯Êý»ñÈ¡applicationµÄÅäÖã¬È»ºóµ÷ÓÃpecan.make_app()º¯Êý´´½¨ÁËÒ»¸öWSGI application£¬×îºóµ÷ÓÃÁËauth.install()º¯Êý£¨Ò²¾ÍÊÇmagnum.api.auth.install()º¯Êý£©Îª¸Õ¸ÕÉú³ÉµÄWSGI application¼ÓÉÏKeystoneµÄÈÏÖ¤Öмä¼þ£¨È·±£ËùÓеÄÇëÇó¶¼»áͨ¹ýKeystoneÈÏÖ¤£©¡£

µ½Õâ±ßΪֹ£¬Ò»¸öPecanµÄWSGI application¾ÍÒѾ­×¼±¸ºÃÁË£¬Ö»Òªµ÷ÓÃÕâ¸ösetup_app()º¯Êý¾ÍÄÜ»ñµÃ¡£ÖÁÓÚÈçºÎ²¿ÊðÕâ¸öWSGI application£¬Çë²Î¿¼WSGI¼ò½éÕâÆªÎÄÕ¡£

´ÓMagnumÕâ¸öʵ¼ÊµÄÀý×Ó¿ÉÒÔ¿´³ö£¬Ê¹ÓÃÁËPecanÖ®ºó£¬ÎÒÃDz»ÔÙÐèÒª×Ô¼ºÐ´ÄÇЩÈßÓàµÄWSGI application´úÂëÁË£¬Ö±½Óµ÷ÓÃPecanµÄmake_app()º¯Êý¾ÍÄÜÍê³ÉÕâЩ¹¤×÷¡£ÁíÍ⣬¶ÔÓÚ֮ǰʹÓÃPasteDeployʱÓõ½µÄºÜ¶àWSGIÖмä¼þ£¬¿ÉÒÔÑ¡ÔñʹÓÃPecanµÄhooks»úÖÆÀ´ÊµÏÖ£¬Ò²Ñ¡ÔñʹÓÃWSGIÖмä¼þµÄ·½Ê½À´ÊµÏÖ¡£ÔÚMagnumµÄAPI·þÎñ¾ÍͬʱʹÓÃÁËÕâÁ½ÖÖ·½Ê½¡£Æäʵ£¬Pecan»¹¿ÉÒÔºÍPasteDeployÒ»ÆðʹÓã¬CeilometerÏîÄ¿¾ÍÊÇÕâô×öµÄ£¬´ó¼Ò¿ÉÒÔ¿´¿´¡£

È·¶¨Â·ÓɱäµÃÈÝÒ×ÁË£º¶ÔÏó·Ö·¢Ê½µÄ·ÓÉ

Pecan²»½öËõ¼õÁËÉú³ÉWSGI applicationµÄ´úÂ룬¶øÇÒÒ²Èÿª·¢ÈËÔ±¸üÈÝÒ×µÄÖ¸¶¨Ò»¸öapplicationµÄ·ÓÉ¡£Pecan²ÉÓÃÁËÒ»ÖÖ¶ÔÏó·Ö·¢·ç¸ñ£¨object-dispatch style£©µÄ·ÓÉģʽ¡£ÎÒÃÇÖ±½Óͨ¹ýÀý×ÓÀ´½âÊÍÕâÖÖ·ÓÉģʽ£¬»¹ÊÇÒÔMagnumÏîĿΪÀý¡£

ÉÏÃæÌáµ½ÁË£¬MagnumµÄAPI·þÎñµÄrootÊÇmagnum.api.controllers.root.RootController¡£ÕâÀïµÄRootControllerµÄÊÇÒ»¸öÀ࣬ÎÒÃÇÀ´¿´ËüµÄ´úÂ룺

class RootController(rest.RestController):

_versions = ['v1']
"""All supported API versions"""

_default_version = 'v1'
"""The default API version"""

v1 = v1.Controller()

@expose.expose(Root)
def get(self):
# NOTE: The reason why convert() it's being called for every
# request is because we need to get the host url from
# the request object to make the links.
return Root.convert()

@pecan.expose()
def _route(self, args):
string">"""Overrides the default routing behavior.

It redirects the request to the default version of the magnum API
if the version number is not specified in the url.
"""

if args[0] and args[0] not in self._versions:
args = [self._default_version] + args
return super(RootController, self)._route(args)

±ð¿´Õâ¸öÀàÕâô³¤£¬ÎÒÀ´½âÊÍÒ»ÏÂÄã¾Í¶®ÁË¡£Ê×ÏÈ£¬Äã¿ÉÒÔÏȺöÂÔµô_route()º¯Êý£¬Õâ¸öº¯ÊýÊÇÓÃÀ´¸²¸ÇPecanµÄĬÈÏ·ÓÉʵÏֵģ¬ÔÚÕâÀïÈ¥µôËü²»·Á°­ÎÒÃÇÀí½âPecan£¨ÕâÀïµÄ_route()º¯ÊýµÄ×÷ÓðÑËùÓÐÇëÇóÖØ¶¨Ïòµ½Ä¬ÈϵÄAPI°æ±¾È¥£©¡£È¥µô_route()ºÍÆäËûµÄ¶«Î÷ºó£¬Õû¸öÀà¾Í±ä³ÉÕâô¶Ì£º

class RootController(rest.RestController):
v1 = v1.Controller()

@expose.expose(Root)
def get(self):
return Root.convert()

1.Ê×ÏÈ£¬ÄãÒª¼Çס£¬Õâ¸öRootController¶ÔÓ¦µÄÊÇURLÖиù·¾¶£¬Ò²¾ÍÊÇpathÖÐ×î×ó±ßµÄ/¡£

2.RootController¼Ì³Ð×Ôrest.RestController£¬ÊÇPecanʵÏÖµÄRESTful¿ØÖÆÆ÷¡£ÕâÀïµÄget()º¯Êý±íʾ£¬µ±·ÃÎʵÄÊÇGET /ʱ£¬Óɸú¯Êý´¦Àí¡£get()º¯Êý»á·µ»ØÒ»¸öWSME¶ÔÏ󣬱íʾһ¸öÐÎʽ»¯µÄHTTP Response£¬Õâ¸öÏÂÃæÔÙ½²¡£get()º¯ÊýÉÏÃæµÄexpose×°ÊÎÆ÷ÊÇPecanʵÏÖ·ÓÉ¿ØÖƵÄÒ»¸ö·½Ê½£¬±»exposeµÄº¯Êý²Å»á±»Â·ÓÉ´¦Àí¡£

3.ÕâÀïµÄv1 = v1.Controller()±íʾ£¬µ±·ÃÎʵÄÊÇGET /v1»òÕßGET /v1/¡­Ê±£¬ÇëÇóÓÉÒ»¸öv1.ControllerʵÀýÀ´´¦Àí¡£

ΪÁ˼ÓÉî´ó¼ÒµÄÀí½â£¬ÎÒÃÇÔÙÀ´¿´ÏÂv1.ControllerµÄʵÏÖ£º

class Controller(rest.RestController):
"""Version 1 API controller root."""

bays = bay.BaysController()
baymodels = baymodel.BayModelsController()
containers = container.ContainersController()
nodes = node.NodesController()
pods = pod.PodsController()
rcs = rc.ReplicationControllersController()
services = service.ServicesController()
x509keypairs = x509keypair.X509KeyPairController()
certificates = certificate.CertificateController()

@expose.expose(V1)
def get(self):
return V1.convert()

...

ÉÏÃæÕâ¸öControllerÒ²ÊǼ̳Ð×Ôrest.RestController¡£ËùÒÔËüµÄgetº¯Êý±íʾ£¬µ±·ÃÎʵÄÊÇGET /v1µÄʱºò£¬Òª×öµÄ´¦Àí¡£È»ºó£¬Ëü»¹ÓкܶàÀàÊôÐÔ£¬ÕâЩÊôÐÔ·Ö±ð±íʾ²»Í¬URL·¾¶µÄ¿ØÖÆÆ÷£º

1./v1/bays ÓÉbays´¦Àí

2./v1/baymodels ÓÉbaymodels´¦Àí

3./v1/containers ÓÉcontainers´¦Àí

ÆäËûµÄ¶¼ÊÇÀàËÆµÄ¡£ÎÒÃÇÔÙ¼ÌÐø¿´bay.BaysControllerµÄ´úÂ룺

class BaysController(rest.RestController):
"""REST controller for Bays."""
def __init__(self):
super(BaysController, self).__init__()

_custom_actions = {
'detail': ['GET'],
}

def get_all(...):

def detail(...):

def get_one(...):

def post(...):

def patch(...):

def delete(...):

Õâ¸öcontrollerÖÐÖ»Óк¯Êý£¬Ã»ÓÐÈκÎÀàÊôÐÔ£¬¶øÇÒûÓÐʵÏÖÈκÎÌØÊâ·½·¨£¬ËùÒÔ/v1/bays¿ªÍ·µÄURL´¦Àí¶¼ÔÚÕâ¸öcontrollerÖÐÖսᡣÕâ¸öÀà»á´¦ÀíÈçÏÂÇëÇó£º

GET /v1/bays 
GET /v1/bays/{UUID}
POST /v1/bays
PATCH /v1/bays/{UUID}
DELETE /v1/bays/{UUID}
GET /v1/bays/detail/{UUID}

¿´ÁËÉÏÃæµÄ3¸öcontrollerÖ®ºó£¬ÄãÓ¦¸ÃÄÜ´ó¸ÅÃ÷°×PecanÊÇÈçºÎ¶ÔURL½øÐзÓɵġ£ÕâÖÖ·ÓÉ·½Ê½¾ÍÊǶÔÏó·Ö·¢£º¸ù¾ÝÀàÊôÐÔ£¬°üÀ¨Êý¾ÝÊôÐԺͷ½·¨ÊôÐÔÀ´¾ö¶¨ÈçºÎ·ÓÉÒ»¸öHTTPÇëÇó¡£PecanµÄÎĵµÖжÔÇëÇóµÄ·ÓÉÓÐרÃŵÄÃèÊö£¬ÒªÏëÕÆÎÕPecanµÄ·ÓÉ»¹ÊÇÒªÍêÕûµÄ¿´Ò»Ï¹ٷ½Îĵµ¡£

ÄÚÖÃRESTful

Ö§³ÖÎÒÃÇÉÏÃæ¾ÙÀýµÄcontroller¶¼ÊǼ̳Ð×Ôpecan.rest.RestController£¬ÕâÖÖcontroller³ÆÎªRESTful controller£¬×¨ÃÅÓÃÓÚʵÏÖRESTful APIµÄ£¬Òò´ËÔÚ£¬OpenStackÖÐʹÓÃÌØ±ð¶à¡£Pecan»¹Ö§³ÖÆÕͨµÄcontroller£¬³ÆÎªGeneric controller¡£Generic controller¼Ì³Ð×Ôobject¶ÔÏó£¬Ä¬ÈÏûÓÐʵÏÖ¶ÔRESTfulÇëÇóµÄ·½·¨¡£¼òµ¥µÄ˵£¬RESTful controller°ïÎÒÃǹ涨ºÃÁËget_one()£¬get_all()£¬ get()£¬post()µÈ·½·¨¶ÔÓ¦µÄHTTPÇëÇ󣬶øGeneric controllerÔòûÓС£¹ØÓÚÕâÁ½ÖÖcontrollerµÄÇø±ð£¬¿ÉÒÔ¿´¹Ù·½Îĵµ¡¶Writing RESTful Web Services with Generic Controllers¡·£¬ÓкÜÇå³þµÄʾÀý¡£

¶ÔÓÚRestControllerÖÐûÓÐÔ¤Ïȶ¨ÒåºÃµÄ·½·¨£¬ÎÒÃÇ¿ÉÒÔͨ¹ý¿ØÖÆÆ÷µÄ_custom_actionsÊôÐÔÀ´Ö¸¶¨ÆäÄÜ´¦ÀíµÄ·½·¨¡£

class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
}

@expose()
def test(self):
return 'hello'

ÉÏÃæÕâ¸ö¿ØÖÆÆ÷ÊÇÒ»¸ö¸ù¿ØÖÆÆ÷£¬Ö¸¶¨ÁË/test·¾¶Ö§³ÖGET·½·¨£¬Ð§¹ûÈçÏ£º

$ curl http://localhost:8080/test
hello%

ÄÇôHTTPÇëÇóºÍHTTPÏìӦĨ£¿

ÉÏÃæ½²ÁËÕâô¶à£¬ÎÒÃǶ¼Ã»ÓÐ˵Ã÷ÔÚPecanÖÐÈçºÎ´¦ÀíÇëÇóºÍÈçºÎ·µ»ØÏìÓ¦¡£Õâ¸ö½«ÔÚÏÂÒ»ÕÂÖÐ˵Ã÷£¬Í¬Ê±ÎÒÃÇ»áÒýÈëÒ»¸öеĿâWSME¡£

WSME

Pecan¶ÔÇëÇóºÍÏìÓ¦µÄ´¦Àí

ÔÚ¿ªÊ¼Ìáµ½WSME֮ǰ£¬ÎÒÃÇÏÈÀ´¿´ÏÂPecan×Ô¼º¶ÔHTTPÇëÇóºÍÏìÓ¦µÄ´¦Àí¡£ÕâÑùÄãÄܸüºÃµÄÀí½âΪʲô»áÔÙÒýÈëÒ»¸öWSME¿â¡£

Pecan¿ò¼ÜΪÿ¸öÏß³Ìά»¤Á˵¥¶ÀµÄÇëÇóºÍÏìÓ¦¶ÔÏó£¬Äã¿ÉÒÔÖ±½ÓÔÚÇëÇó´¦Àíº¯ÊýÖзÃÎÊ¡£pecan.requestºÍpecan.response·Ö±ð´ú±íµ±Ç°ÐèÒª´¦ÀíµÄÇëÇóºÍÏìÓ¦¶ÔÏó¡£Äã¿ÉÒÔÖ±½Ó²Ù×÷ÕâÁ½¸ö¶ÔÏ󣬱ÈÈçÖ¸¶¨ÏìÓ¦µÄ״̬Â룬¾ÍÏñÏÂÃæÕâ¸öÀý×ÓÒ»Ñù£¨Àý×ÓÀ´×Ô¹Ù·½Îĵµ£©£º

@pecan.expose()
def login(self):
assert pecan.request.path == '/login'
username = pecan.request.POST.get('username')
password = pecan.request.POST.get('password')

pecan.response.status = 403
pecan.response.text = 'Bad Login!'

Õâ¸öÀý×ÓÑÝʾÁË·ÃÎÊPOSTÇëÇóµÄ²ÎÊýÒÔ¼°·µ»Ø403¡£ÄãÒ²¿ÉÒÔÖØÐ¹¹ÔìÒ»¸öpecan.Response¶ÔÏó×÷Ϊ·µ»ØÖµ£¨Àý×ÓÀ´×Ô¹Ù·½Îĵµ£©£º

from pecan import expose, Response

class RootController(object):

@expose()
def hello(self):
return Response('Hello, World!', 202)

ÁíÍ⣬HTTPÇëÇóµÄ²ÎÊýÒ²»á¿ÉÒÔ×÷Ϊ¿ØÖÆÆ÷·½·¨µÄ²ÎÊý£¬»¹ÊÇÀ´¿´¼¸¸ö¹Ù·½ÎĵµµÄÀý×Ó£º

class RootController(object):
@expose()
def index(self, arg):
return arg

@expose()
def kwargs(self, **kwargs):
return str(kwargs)

Õâ¸ö¿ØÖÆÆ÷Öеķ½·¨Ö±½Ó·µ»ØÁ˲ÎÊý£¬ÑÝʾÁ˶ÔGETÇëÇó²ÎÊýµÄ´¦Àí£¬Ð§¹ûÊÇÕâÑùµÄ£º

$ curl http://localhost:8080/?arg=foo
foo
$ curl http://localhost:8080/kwargs?a=1&b=2&c=3
{u'a': u'1', u'c': u'3', u'b': u'2'}

ÓÐʱºò£¬²ÎÊýÒ²¿ÉÄÜÊÇURLµÄÒ»²¿·Ö£¬±ÈÈç×îºóµÄÒ»¶Îpath×÷Ϊ²ÎÊý£¬¾ÍÏñÏÂÃæÕâÑù£º

class RootController(object):
@expose()
def args(self, *args):
return ','.join(args)

Ч¹ûÊÇÕâÑùµÄ£º

$ curl http://localhost:8080/args/one/two/three
one,two,three

ÁíÍ⣬ÎÒÃÇ»¹Òª¿´Ò»ÏÂPOST·½·¨µÄ²ÎÊýÈçºÎ´¦Àí£¨Àý×ÓÀ´×Ô¹Ù·½Îĵµ£©£º

class RootController(object):
@expose()
def index(self, arg):
return arg

Ч¹ûÈçÏ£¬¾ÍÊǰÑHTTP body½âÎö³ÉÁË¿ØÖÆÆ÷·½·¨µÄ²ÎÊý£º

$ curl -X POST "http://localhost:8080/" -H "Content-Type: 
application/x-www-form-urlencoded" -d "arg=foo" foo

·µ»ØJSON»¹ÊÇHTML£¿

Èç¹ûÄã²»ÊÇÃ÷È·µÄ·µ»ØÒ»¸öResponse¶ÔÏó£¬ÄÇôPecanÖз½·¨µÄ·µ»ØÄÚÈÝÀàÐ;ÍÊÇÓÉexpose()×°ÊÎÆ÷¾ö¶¨µÄ¡£Ä¬ÈÏÇé¿öÏ£¬¿ØÖÆÆ÷µÄ·½·¨·µ»ØµÄcontent-typeÊÇHTML¡£

class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
}

@expose()
def test(self):
return 'hello'

Ч¹ûÈçÏ£º

 $ curl -v http://localhost:8080/test
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8080
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Tue, 15 Sep 2015 14:31:28 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 5
< Content-Type: text/html; charset=UTF-8
<
* Closing connection 0
hello%

Ò²¿ÉÒÔÈÃËü·µ»ØJSON£º

class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
}

@expose('json')
def test(self):
return 'hello'

Ч¹ûÈçÏ£º

curl -v http://localhost:8080/test
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8080
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Tue, 15 Sep 2015 14:33:27 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 18
< Content-Type: application/json; charset=UTF-8
<
* Closing connection 0
{"hello": "world"}%

ÉõÖÁ£¬Ä㻹¿ÉÒÔÈÃÒ»¸ö¿ØÖÆÆ÷·½·¨¸ù¾ÝURL pathµÄÀ´¾ö¶¨ÊÇ·µ»ØHTML»¹ÊÇJSON£º

class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
}

@expose()
@expose('json')
def test(self):
return json.dumps({'hello': 'world'})

·µ»ØJSON£º

$ curl -v http://localhost:8080/test.json
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test.json HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8080
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Wed, 16 Sep 2015 14:26:27 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 24
< Content-Type: application/json; charset=UTF-8
<
* Closing connection 0
"{\"hello\": \"world\"}"%

·µ»ØHTML£º

$ curl -v http://localhost:8080/test.html
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test.html HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8080
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Wed, 16 Sep 2015 14:26:24 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 18
< Content-Type: text/html; charset=UTF-8
<
* Closing connection 0
{"hello": "world"}%

ÕâÀïҪעÒâһϣº

ͬһ¸ö×Ö·û´®×÷ΪJSON·µ»ØºÍ×÷ΪHTML·µ»ØÊDz»Ò»ÑùµÄ£¬×Ðϸ¿´Ò»ÏÂHTTPÏìÓ¦µÄÄÚÈÝ¡£

ÎÒÃǵÄÀý×ÓÖÐÔÚURLµÄ×îºó¼ÓÉÏÁË.htmlºó׺»òÕß.jsonºó׺£¬Çë³¢ÊÔһϲ»¼Óºó׺µÄ»¯ÊÇ·µ»ØÊ²Ã´£¿È»ºó£¬µ÷»»Ò»ÏÂÁ½¸öexpose()µÄ˳ÐòÔÙÊÔһϡ£

´ÓÉÏÃæµÄÀý×Ó¿ÉÒÔ¿´³ö£¬¾ö¶¨ÏìÓ¦ÀàÐ͵ÄÖ÷ÒªÊÇ´«µÝ¸øexpose()º¯ÊýµÄ²ÎÊý£¬ÎÒÃÇ¿´ÏÂexpose()º¯ÊýµÄÍêÕûÉùÃ÷£º

pecan.decorators.expose(template=None,
content_type='text/html',
generic=False)

template²ÎÊýÓÃÀ´Ö¸¶¨·µ»ØÖµµÄÄ£°å£¬Èç¹ûÊÇ¡¯json¡¯¾Í»á·µ»ØJSONÄÚÈÝ£¬ÕâÀï¿ÉÒÔÖ¸¶¨Ò»¸öHTMLÎļþ£¬»òÕßÖ¸¶¨Ò»¸ömakoÄ£°å¡£

content_typeÖ¸¶¨ÏìÓ¦µÄcontent-type£¬Ä¬ÈÏÖµÊÇ¡¯text/html¡¯¡£

generic²ÎÊý±íÃ÷¸Ã·½·¨ÊÇÒ»¸ö¡°·ºÐÍ¡±·½·¨£¬¿ÉÒÔÖ¸¶¨¶à¸ö²»Í¬µÄº¯Êý¶ÔӦͬһ¸ö·¾¶µÄ²»Í¬µÄHTTP·½·¨¡£

¿´¹ý²ÎÊýµÄ½âÊͺó£¬ÄãÓ¦¸ÃÄÜ´ó¸ÅÁ˽âexpose()º¯ÊýÊÇÈçºÎ¿ØÖÆHTTPÏìÓ¦µÄÄÚÈݺÍÀàÐ͵ġ£

ÓÃWSMEÀ´×öʲô£¿

ÉÏÃæÁ½½ÚÒѾ­ËµÃ÷ÁËPecan¿ÉÒԱȽϺõĴ¦ÀíHTTPÇëÇóÖеIJÎÊýÒÔ¼°¿ØÖÆHTTP·µ»ØÖµ¡£ÄÇôΪʲôÎÒÃÇ»¹ÐèÒªWSMEÄØ£¿ÒòΪPecanÔÚ×öÏÂÃæÕâ¸öÊÂÇéµÄʱºò±È½ÏÂé·³£ºÇëÇó²ÎÊýºÍÏìÓ¦ÄÚÈݵÄÀàÐͼì²é£¨Ó¢Îļò³Æ¾ÍÊÇtyping£©¡£µ±È»£¬×öÊÇ¿ÉÒÔ×öµÄ£¬²»¹ýÄãÐèÒª×Ô¼º·ÃÎÊpecan.requestºÍpecan.response£¬È»ºó¼ì²éÖ¸¶¨µÄÖµµÄÀàÐÍ¡£WSME¾ÍÊÇΪ½â¾öÕâ¸öÎÊÌâ¶øÉúµÄ£¬¶øÇÒÊÊÓó¡¾°¾ÍÊÇRESTful API¡£

WSME¼ò½é

WSMEµÄÈ«³ÆÊÇWeb Service Made Easy£¬ÊÇרÃÅÓÃÓÚʵÏÖREST·þÎñµÄtyping¿â£¬ÈÃÄã²»ÐèÒªÖ±½Ó²Ù×÷ÇëÇóºÍÏìÓ¦£¬¶øÇҸպúÍPecan½áºÏµÃ·Ç³£ºÃ£¬ËùÒÔ£¬OpenStackµÄºÜ¶àÏîÄ¿¶¼Ê¹ÓÃÁËPecan + WSMEµÄ×éºÏÀ´ÊµÏÖAPI£¨ºÃ°É£¬ÎÒ¿´¹ýµÄÏîÄ¿£¬ÓÃÁËPecanµÄ¶¼ÓÃÁËWSME£©¡£WSMEµÄÀíÄîÊÇ£ºÔڴ󲿷ÖÇé¿öÏ£¬Web·þÎñµÄÊäÈëºÍÊä³ö¶ÔÊý¾ÝÀàÐ͵ÄÒªÇó¶¼ÊÇÑϸñµÄ¡£ËùÒÔËü¾ÍרÃŽâ¾öÁËÕâ¸öÊÂÇ飬Ȼºó°ÑÆäËûÊÂÇé¶¼½»¸øÆäËû¿ò¼ÜȥʵÏÖ¡£Òò´Ë£¬Ò»°ãWSME¶¼ÊÇºÍÆäËû¿ò¼ÜÅäºÏʹÓõģ¬Ö§³ÖPecan¡¢FlaskµÈ¡£WSMEµÄÎĵµµØÖ·¡£

WSMEµÄʹÓÃ

ÓÃÁËWSMEºóµÄºÃ´¦ÊÇÊ²Ã´ÄØ£¿WSME»á×Ô¶¯°ïÄã¼ì²éHTTPÇëÇóºÍÏìÓ¦ÖеÄÊý¾ÝÊÇ·ñ·ûºÏÔ¤ÏÈÉ趨ºÃµÄÒªÇó¡£WSMEµÄÖ÷Òª·½Ê½ÊÇͨ¹ý×°ÊÎÆ÷À´¿ØÖÆcontroller·½·¨µÄÊäÈëºÍÊä³ö¡£WSMEÖÐÖ÷ҪʹÓÃÁ½¸ö¿ØÖÆÆ÷£º

@signature: Õâ¸ö×°ÊÎÆ÷ÓÃÀ´ÃèÊöÒ»¸öº¯ÊýµÄÊäÈëºÍÊä³ö¡£

@wsexpose: Õâ¸ö×°ÊÎÆ÷°üº¬@signatureµÄ¹¦ÄÜ£¬Í¬Ê±»á°Ñº¯ÊýµÄ·ÓÉÐÅÏ¢±©Â¶¸øWeb¿ò¼Ü£¬Ð§¹û¾ÍÏñPecanµÄexpose×°ÊÎÆ÷¡£

ÕâÀïÎÒÃǽáºÏPecanÀ´½²½âWSMEµÄʹÓá£ÏÈÀ´¿´Ò»¸öԭʼÀàÐ͵ÄÀý×Ó£º

from wsmeext.pecan import wsexpose

class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
}

@wsexpose(int, int)
def test(self, number):
return number

Èç¹û²»Ìṩ²ÎÊý£¬·ÃÎÊ»áʧ°Ü£º

$ curl http://localhost:8080/test
{"debuginfo": null, "faultcode": "Client", "faultstring": "Missing argument: \"number\""}%

Èç¹ûÌṩµÄ²ÎÊý²»ÊÇÕûÐÍ£¬·ÃÎÊÒ²»áʧ°Ü£º

$ curl http://localhost:8080/test\?number\=a
{"debuginfo": null, "faultcode": "Client", "faultstring": "Invalid
input for field/attribute number. Value: 'a'. unable to convert to int"}%

ÉÏÃæÕâЩ´íÎóÐÅÏ¢¶¼ÊÇÓÉWSME¿ò¼ÜÖ±½Ó·µ»ØµÄ£¬»¹Ã»ÓÐÖ´Ðе½ÄãдµÄ·½·¨¡£Èç¹ûÇëÇóÕýÈ·£¬ÄÇô»áÊÇÕâÑùµÄ£º

$ curl -v http://localhost:8080/test\?number\=1
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test?number=1 HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8080
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Wed, 16 Sep 2015 15:06:35 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 1
< Content-Type: application/json; charset=UTF-8
<
* Closing connection 0
1%

Çë×¢Òâ·µ»ØµÄcontent-type£¬ÕâÀï·µ»ØJSONÊÇÒòΪÎÒÃÇʹÓõÄwsexposeÉèÖõķµ»ØÀàÐÍÊÇXMLºÍJSON£¬²¢ÇÒJSONÊÇĬÈÏÖµ¡£ÉÏÃæÕâ¸öÀý×Ó¾ÍÊÇWSME×î¼òµ¥µÄÓ¦ÓÃÁË¡£

ÄÇôÏÖÔÚÓÐÏÂÃæÕâЩÎÊÌâÐèҪ˼¿¼Ò»Ï£º

Èç¹ûÏëÓÃPOSTµÄ·½Ê½À´´«µÝ²ÎÊý£¬ÒªÔõô×öÄØ£¿Ìáʾ£ºÒªÔĶÁWSMEÖÐ@signature×°ÊÎÆ÷µÄÎĵµ¡£

Èç¹ûÎÒÏ£ÍûʹÓÃ/test/1ÕâÖÖ·½Ê½À´´«µÝ²ÎÊýÒªÔõô×öÄØ£¿Ìáʾ£ºÒªÔĶÁPecanÎĵµÖйØÓÚ·ÓɵIJ¿·Ö¡£

WSMEÖÐÖ§³Ö¶ÔÄÄЩÀàÐ͵ļì²éÄØ£¿WSMEÖ§³ÖÕûÐÍ¡¢¸¡µãÐÍ¡¢×Ö·û´®¡¢²¼¶ûÐÍ¡¢ÈÕÆÚʱ¼äµÈ£¬ÉõÖÁ»¹Ö§³ÖÓû§×Ô¶¨ÒåÀàÐÍ¡£Ìáʾ£ºÒªÔĶÁWSMEÎĵµÖйØÓÚÀàÐ͵IJ¿·Ö¡£

WSMEÖ§³ÖÊý×éÀàÐÍô£¿Ö§³Ö¡£

ÉÏÃæµÄÎÊÌâÆäʵҲÊǺܶàÈËʹÓÃWSMEµÄʱºò¾­³£ÎʵÄÎÊÌâ¡£ÎÒÃǽ«ÔÚÏÂһƪÎÄÕÂÖÐʹÓÃPecan + WSMEÀ´¼ÌÐø¿ª·¢ÎÒÃǵÄdemo£¬²¢ÇÒÓôúÂëÀ´»Ø´ðÉÏÃæËùÓеÄÎÊÌâ¡£

ÉÏһƪÎÄÕÂ˵µ½£¬ÎÒÃǽ«ÒÔʵÀýµÄÐÎʽÀ´¼ÌÐø½²ÊöÕâ¸öAPI·þÎñµÄ¿ª·¢ÖªÊ¶£¬ÕâÀï»áʹÓÃPecanºÍWSMEÁ½¸ö¿â¡£

Éè¼ÆREST API

Òª¿ª·¢REST API·þÎñ£¬ÎÒÃÇÊ×ÏÈÐèÒªÉè¼ÆÒ»ÏÂÕâ¸ö·þÎñ¡£Éè¼Æ°üÀ¨ÒªÊµÏֵŦÄÜ£¬ÒÔ¼°½Ó¿ÚµÄ¾ßÌ广·¶¡£ÎÒÃÇÕâÀïҪʵÏÖµÄÊÇÒ»¸ö¼òµ¥µÄÓû§¹ÜÀí½Ó¿Ú£¬°üÀ¨Ôöɾ¸Ä²éµÈ¹¦ÄÜ¡£Èç¹û¶ÁÕß¶ÔREST API²»ÊìϤ£¬¿ÉÒÔÏÈ´ÓWikiÒ³ÃæÁ˽âһϡ£

ÁíÍ⣬ΪÁË·½±ã´ó¼ÒÔĶÁºÍÀí½â£¬±¾ÏµÁеĴúÂë»á·ÅÔÚgithubÉÏ£¬diabloneo/webdemo¡£

Version of REST API

ÔÚOpenStackµÄÏîÄ¿ÖУ¬¶¼ÊÇÔÚURLÖбíÃ÷Õâ¸öAPIµÄ°æ±¾ºÅµÄ£¬±ÈÈçKeystoneµÄAPI»áÓÐ/v2.0ºÍ/v3µÄǰ׺£¬±íÃ÷Á½¸ö²»Í¬°æ±¾µÄAPI£»MagnumÏîĿĿǰµÄAPIÔòΪv1°æ±¾¡£ÒòΪÎÒÃǵÄwebdemoÏîÄ¿²Å¸Õ¸Õ¿ªÊ¼£¬ËùÒÔÎÒÃÇÒ²°ÑÎÒÃǵÄAPI°æ±¾ÉèÖÃΪv1£¬ÏÂÎÄ»á˵Ã÷ÔõôʵÏÖÕâ¸översionºÅµÄÉèÖá£

REST API of Users

ÎÒÃǽ«ÒªÉè¼ÆÒ»¸ö¹ÜÀíÓû§µÄAPI£¬Õâ¸öºÍKeystoneµÄÓû§¹ÜÀíµÄAPI²î²»¶à£¬ÕâÀïÏÈÁгöÿ¸öAPIµÄÐÎʽ£¬ÒÔ¼°¼òÒªµÄÄÚÈÝ˵Ã÷¡£ÕâÀïÎÒÃÇ»á°ÑÉÏÃæÌáµ½µÄversionºÅÒ²¼ÓÈëµ½URL pathÖУ¬ÈöÁÕßÄܸüÈÝÒ×ÁªÏµÆðÀ´¡£

GET /v1/users »ñÈ¡ËùÓÐÓû§µÄÁÐ±í¡£

POST /v1/users ´´½¨Ò»¸öÓû§¡£

GET /v1/users/ »ñȡһ¸öÌØ¶¨Óû§µÄÏêϸÐÅÏ¢¡£

PUT /v1/users/ ÐÞ¸ÄÒ»¸öÓû§µÄÏêϸÐÅÏ¢¡£

DELETE /v1/users/ ɾ³ýÒ»¸öÓû§¡£

ÕâЩ¾ÍÊÇÎÒÃÇҪʵÏÖµÄÓû§¹ÜÀíµÄAPIÁË¡£ÆäÖУ¬±íʾʹÓÃÒ»¸öUUID×Ö·û´®£¬Õâ¸öÊÇOpenStackÖÐ×î¾­³£±»ÓÃÀ´×÷Ϊ¸÷ÖÖ×ÊÔ´IDµÄÐÎʽ£¬ÈçÏÂËùʾ£º

In [5]: import uuid
In [6]: print uuid.uuid4()
adb92482-baab-4832-84bc-f842f3eabd66
In [7]: print uuid.uuid4().hex
29520c88de6b4c76ae8deb48db0a71e7

ÒòΪÊǸödemo£¬ËùÒÔÎÒÃÇÉèÖÃÒ»¸öÓû§°üº¬µÄÐÅÏ¢»á±È½Ï¼òµ¥£¬Ö»°üº¬nameºÍage¡£

ʹÓÃPecan´î½¨API·þÎñµÄ¿ò¼Ü½ÓÏÂÀ´¾ÍÒª¿ªÊ¼±àÂ빤×÷ÁË¡£Ê×ÏÈÒª°ÑÕû¸ö·þÎñµÄ¿ò¼Ü´î½¨ÆðÀ´¡£ÎÒÃÇ»áÔÚÈí¼þ°ü¹ÜÀíÕâÆªÎļþÖеĴúÂë»ù´¡ÉϼÌÐøÎÒÃǵÄdemo£¨ËùÓÐÕâЩ´úÂëÔÚgithubµÄ²Ö¿âÀï¶¼ÄÜ¿´µ½£©¡£

´úÂëĿ¼½á¹¹

Ò»°ãÀ´Ëµ£¬OpenStackÏîÄ¿ÖУ¬Ê¹ÓÃPecanÀ´¿ª·¢API·þÎñʱ£¬¶¼»áÔÚ´úÂëĿ¼ÏÂÓÐÒ»¸öרÃŵÄAPIĿ¼£¬ÓÃÀ´±£´æAPIÏà¹ØµÄ´úÂë¡£±ÈÈçMagnumÏîÄ¿µÄmagnum/api£¬»òÕßCeilometerÏîÄ¿µÄceilometer/apiµÈ¡£ÎÒÃǵĴúÂëÒ²×ñÊØÕâ¸ö¹æ·¶£¬ÈÃÎÒÃÇÖ±½ÓÀ´¿´ÏÂÎÒÃǵĴúÂëĿ¼½á¹¹£¨#ºóÃæµÄ±íʾעÊÍ£©£º

-> ~/programming/python/webdemo/webdemo/api git:(master) ? $ tree .
.
©À©¤©¤ app.py # Õâ¸öÎļþ´æ·ÅWSGI applicationµÄÈë¿Ú
©À©¤©¤ config.py # Õâ¸öÎļþ´æ·ÅPecanµÄÅäÖÃ
©À©¤©¤ controllers/ # Õâ¸öĿ¼ÓÃÀ´´æ·ÅPecan¿ØÖÆÆ÷µÄ´úÂë
©À©¤©¤ hooks.py # Õâ¸öÎļþ´æ·ÅPecanµÄhooks´úÂ루±¾ÎÄÖÐÓò»µ½£©
©¸©¤©¤ __init__.py

Õâ¸öÔÚAPI·þÎñ(3)ÕâÆªÎÄÕÂÖÐÒѾ­ËµÃ÷¹ýÁË¡£

ÏÈÈÃÎÒÃǵķþÎñÅÜÆðÀ´ÎªÁ˺óÃæ¸üºÃµÄ¿ª·¢£¬ÎÒÃÇÐèÒªÏÈÈÃÎÒÃǵķþÎñÔÚ±¾µØÅÜÆðÀ´£¬ÕâÑù¿ÉÒÔ·½±ã×Ô¼º×ö²âÊÔ£¬¿´µ½´úÂëµÄЧ¹û¡£²»¹ýÒª×öµ½Õâµã£¬»¹ÊÇÓÐЩ¸´Ôӵġ£

±ØÒªµÄ´úÂë

Ê×ÏÈ£¬ÏÈ´´½¨config.pyÎļþµÄÄÚÈÝ£º

app = {
'root': 'webdemo.api.controllers.root.RootController',
'modules': ['webdemo.api'],
'debug': False,
}

¾ÍÊǰüº¬ÁËPecanµÄ×î»ù±¾ÅäÖã¬ÆäÖÐÖ¸¶¨ÁËroot controllerµÄλÖá£È»ºó¿´ÏÂapp.pyÎļþµÄÄÚÈÝ£¬Ö÷Òª¾ÍÊǶÁÈ¡config.pyÖеÄÅäÖã¬È»ºó´´½¨Ò»¸öWSGI application£º

import pecan

from webdemo.api import config as api_config


def get_pecan_config():
filename = api_config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename)


def setup_app():
config = get_pecan_config()

app_conf = dict(config.app)
app = pecan.make_app(
app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
**app_conf
)

return app

È»ºó£¬ÎÒÃÇÖÁÉÙ»¹ÐèҪʵÏÖÒ»ÏÂroot controller£¬Ò²¾ÍÊÇwebdemo/api/controllers/root.pyÕâ¸öÎļþÖеÄRootControllerÀࣺ

from pecan import rest
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan


class RootController(rest.RestController):

@wsme_pecan.wsexpose(wtypes.text)
def get(self):
return "webdemo"

±¾µØ²âÊÔ·þÎñÆ÷

ΪÁ˼ÌÐø¿ª·ÅµÄ·½±ã£¬ÎÒÃÇÒªÏÈ´´½¨Ò»¸öPython½Å±¾£¬¿ÉÒÔÆô¶¯Ò»¸öµ¥½ø³ÌµÄAPI·þÎñ¡£Õâ¸ö½Å±¾»á·ÅÔÚwebdemo/cmd/Ŀ¼Ï£¬Ãû³ÆÊÇapi.py£¨ÕâĿ¼ºÍ½Å±¾Ãû³ÆÒ²ÊǹßÀý£©£¬À´¿´¿´ÎÒÃǵÄapi.py°É£º

from wsgiref import simple_server

from webdemo.api import app


def main():
host = '0.0.0.0'
port = 8080

application = app.setup_app()
srv = simple_server.make_server(host, port, application)

srv.serve_forever()


if __name__ == '__main__':
main()

ÔËÐвâÊÔ·þÎñÆ÷µÄ»·¾³

ÒªÔËÐÐÕâ¸ö²âÊÔ·þÎñÆ÷£¬Ê×ÏÈÐèÒª°²×°±ØÒªµÄ°ü£¬²¢ÇÒÉèÖÃÕýÈ·µÄ·¾¶¡£ÔÚºóÃæµÄÎÄÕÂÖУ¬ÎÒÃǽ«»áÖªµÀ£¬Õâ¸ö¿ÉÒÔͨ¹ýtoxÕâ¸ö¹¤¾ßÀ´ÊµÏÖ¡£ÏÖÔÚ£¬ÎÒÃÇÏÈ×ö¸ö¼òµ¥°æ±¾µÄ£¬¾ÍÊÇÊÖ¶¯´´½¨Õâ¸öÔËÐл·¾³¡£

Ê×ÏÈ£¬ÍêÉÆÒ»ÏÂrequirements.txtÕâ¸öÎļþ£¬°üº¬ÎÒÃÇÐèÒªµÄ°ü£º

pbr<2.0,>=0.11
pecan
WSME

È»ºó£¬ÎÒÃÇÊÖ¶¯´´½¨Ò»¸övirtualenv»·¾³£¬²¢ÇÒ°²×°requirements.txtÖÐÒªÇóµÄ°ü£º

-> ~/programming/python/webdemo git:(master) ? $ virtualenv .venv
New python executable in .venv/bin/python
Installing setuptools, pip, wheel...done.
-> ~/programming/python/webdemo git:(master) ? $ source .venv/bin/activate
(.venv)
-> ~/programming/python/webdemo git:(master) ? $ pip install -r requirement.txt
...
Successfully installed Mako-1.0.3 MarkupSafe-0.23 WSME-0.8.0 WebOb-
1.5.1 WebTest-2.0.20 beautifulsoup4-4.4.1 logutils-0.3.3 netaddr-
0.7.18 pbr-1.8.1 pecan-1.0.3 pytz-2015.7 simplegeneric-0.8.1
singledispatch-3.4.0.3 six-1.10.0 waitress-0.8.10

Æô¶¯ÎÒÃǵķþÎñ

Æô¶¯·þÎñÐèÒª¼¼ÇÉ£¬ÒòΪÎÒÃǵÄwebdemo»¹Ã»Óа²×°µ½ÏµÍ³µÄPython·¾¶ÖУ¬Ò²²»ÔÚÉÏÃæ´´½¨virtualenv»·¾³ÖУ¬ËùÒÔÎÒÃÇÐèҪͨ¹ýÖ¸¶¨PYTHONPATHÕâ¸ö»·¾³±äÁ¿À´ÎªPython³ÌÐòÔö¼Ó¿âµÄ²éÕÒ·¾¶£º

(.venv)-> ~/programming/python/webdemo git:(master) ? $ PYTHONPATH=. python webdemo/cmd/api.py

ÏÖÔÚ²âÊÔ·þÎñÆ÷ÒѾ­ÆðÀ´ÁË£¬¿ÉÒÔͨ¹ýä¯ÀÀÆ÷·ÃÎÊhttp://localhost:8080/ Õâ¸öµØÖ·À´²é¿´½á¹û¡££¨Äã¿ÉÄܻᷢÏÖ£¬·µ»ØµÄÊÇXML¸ñʽµÄ½á¹û£¬¶øÎÒÃÇÏëÒªµÄÊÇJSON¸ñʽµÄ¡£Õâ¸öÊÇWSMEµÄÎÊÌ⣬ÎÒÃǺóÃæÔÙÀ´´¦Àí£©¡£

µ½ÕâÀÎÒÃǵÄREST API·þÎñµÄ¿ò¼ÜÒѾ­´î½¨Íê³É£¬²¢ÇÒ²âÊÔ·þÎñÆ÷Ò²ÅÜÆðÀ´ÁË¡£

Óû§¹ÜÀíAPIµÄʵÏÖ

ÏÖÔÚÎÒÃÇÀ´ÊµÏÖÎÒÃÇÔÚµÚÒ»ÕÂÉè¼ÆµÄAPI¡£ÕâÀïÏÈ˵Ã÷һϣºÎÒÃÇ»áÖ±½ÓʹÓÃPecanµÄRestControllerÀ´ÊµÏÖREST API£¬ÕâÑù¿ÉÒÔ²»ÓÃΪÿ¸ö½Ó¿ÚÖ¸¶¨½ÓÊܵÄmethod¡£

ÈÃAPI·µ»ØJSON¸ñʽµÄÊý¾Ý

ÏÖÔÚ£¬ËùÓеÄOpenStackÏîÄ¿µÄREST APIµÄ·µ»Ø¸ñʽ¶¼ÊÇʹÓÃJSON±ê×¼£¬ËùÒÔÎÒÃÇÒ²ÒªÕâô×ö¡£ÄÇôÓÐʲô°ì·¨Äܹ»ÈÃWSME¿ò¼Ü·µ»ØJSONÊý¾ÝÄØ£¿¿ÉÒÔͨ¹ýÉèÖÃwsmeext.pecan.wsexpose()µÄrest_content_types²ÎÊýÀ´ÊÇÏÈ¡£ÕâÀÎÒÃÇ½è¼øÒ»¶ÎMagnumÏîÄ¿ÖеĴúÂ룬°ÑÕâ¶Î´úÂë´æ·ÅÔÚÎļþwebdemo/api/expose.pyÖУº

import wsmeext.pecan as wsme_pecan


def expose(*args, **kwargs):
"""Ensure that only JSON, and not XML, is supported."""
if 'rest_content_types' not in kwargs:
kwargs['rest_content_types'] = ('json',)

return wsme_pecan.wsexpose(*args, **kwargs)

ÕâÑùÎÒÃǾͷâ×°ÁË×Ô¼ºµÄexpose×°ÊÎÆ÷£¬Ã¿´Î¶¼»áÉèÖÃÏìÓ¦µÄcontent-typeΪJSON¡£ÉÏÃæµÄroot controller´úÂëÒ²¾Í¿ÉÒÔÐÞ¸ÄΪ£º

from pecan import rest
from wsme import types as wtypes

from webdemo.api import expose


class RootController(rest.RestController):

@expose.expose(wtypes.text)
def get(self):
return "webdemo"

ÔÙ´ÎÔËÐÐÎÒÃǵIJâÊÔ·þÎñÆ÷£¬¾Í¿ÉÒÔ·µÏÖ·µ»ØÖµÎªJSON¸ñʽÁË¡£

ʵÏÖ GET /v1Õâ¸öÆäʵ¾ÍÊÇʵÏÖv1Õâ¸ö°æ±¾µÄAPIµÄ·¾¶Ç°×º¡£ÔÚPecanµÄ°ïÖúÏ£¬ÎÒÃǺÜÈÝÒ×ʵÏÖÕâ¸ö£¬Ö»Òª°´ÕÕÈçÏÂÁ½²½×ö¼´¿É£º

ÏÈʵÏÖv1Õâ¸öcontroller

°Ñv1 controller¼ÓÈëµ½root controllerÖÐ

°´ÕÕOpenStackÏîÄ¿µÄ¹æ·¶£¬ÎÒÃÇ»áÏȽ¨Á¢Ò»¸öwebdemo/api/controllers/v1/Ŀ¼£¬È»ºó½«v1 controller·ÅÔÚÕâ¸öĿ¼ÏµÄÒ»¸öÎļþÖУ¬¼ÙÉèÎÒÃǾͷÅÔÚv1/controller.pyÎļþÖУ¬Ð§¹ûÈçÏÂ:

from pecan import rest
from wsme import types as wtypes

from webdemo.api import expose


class V1Controller(rest.RestController):

@expose.expose(wtypes.text)
def get(self):
return 'webdemo v1controller'

È»ºó°ÑÕâ¸öcontroller¼ÓÈëµ½root controllerÖУº

...
from webdemo.api.controllers.v1 import controller as v1_controller
from webdemo.api import expose


class RootController(rest.RestController):
v1 = v1_controller.V1Controller()

@expose.expose(wtypes.text)
def get(self):
return "webdemo"

´Ëʱ£¬Äã·ÃÎÊhttp://localhost:8080/v1¾Í¿ÉÒÔ¿´µ½½á¹ûÁË¡£

ʵÏÖ GET/v1/users

Ìí¼Óusers controller

Õâ¸öAPI¾ÍÊÇ·µ»ØËùÓеÄÓû§ÐÅÏ¢£¬¹¦Äܼܺòµ¥¡£Ê×ÏÈÒªÌí¼Óusers controllerµ½ÉÏÃæµÄv1 controllerÖС£ÎªÁ˲»Ó°ÏìÔĶÁÌåÑ飬ÕâÀï¾Í²»Ìù´úÂëÁË£¬Çë¿´githubÉϵÄʾÀý´úÂë¡£

ʹÓÃWSMEÀ´¹æ·¶APIµÄÏìÓ¦Öµ

ÉÏÆªÎÄÕÂÖУ¬ÎÒÃÇÒѾ­Ìáµ½ÁËWSME¿ÉÒÔÓÃÀ´¹æ·¶APIµÄÇëÇóºÍÏìÓ¦µÄÖµ£¬ÕâÀïÎÒÃǾÍÒªÓÃÉÏËü¡£Ê×ÏÈ£¬ÎÒÃÇÒª²Î¿¼OpenStackµÄ¹ßÀýÀ´Éè¼ÆÕâ¸öAPIµÄ·µ»ØÖµ£º

{
"users": [
{
"name": "Alice",
"age": 30
},
{
"name": "Bob",
"age": 40
}
]
}

ÆäÖÐusersÊÇÒ»¸öÁÐ±í£¬ÁбíÖеÄÿ¸öÔªËØ¶¼ÊÇÒ»¸öuser¡£ÄÇô£¬ÎÒÃÇÒªÈçºÎʹÓÃWSMEÀ´¹æ·¶ÎÒÃǵÄÏìӦֵĨ£¿´ð°¸¾ÍÊÇʹÓÃWSMEµÄ×Ô¶¨ÒåÀàÐÍ¡£ÎÒÃÇ¿ÉÒÔÀûÓÃWSMEµÄÀàÐ͹¦Äܶ¨Òå³öÒ»¸öuserÀàÐÍ£¬È»ºóÔÙ¶¨ÒåÒ»¸öuserµÄÁбíÀàÐÍ¡£×îºó£¬ÎÒÃǾͿÉÒÔʹÓÃÉÏÃæµÄexpose·½·¨À´¹æ¶¨Õâ¸öAPI·µ»ØµÄÊÇÒ»¸öuserµÄÁбíÀàÐÍ¡£

¶¨ÒåuserÀàÐͺÍuserÁбíÀàÐÍ

ÕâÀïÎÒÃÇÐèÒªÓõ½WSMEµÄComplex typesµÄ¹¦ÄÜ£¬ÇëÏÈ¿´Ò»ÏÂÎĵµTypes¡£¼òµ¥Ëµ£¬¾ÍÊÇÎÒÃÇ¿ÉÒÔ°ÑWSMEµÄ»ù±¾ÀàÐÍ×éºÏ³ÉÒ»¸ö¸´ÔÓµÄÀàÐÍ¡£ÎÒÃǵÄÀàÐÍÐèÒª¼Ì³Ð×Ôwsme.types.BaseÕâ¸öÀà¡£ÒòΪÎÒÃÇÔÚ±¾ÎÄÖ»»áʵÏÖÒ»¸öuserÏà¹ØµÄAPI£¬ËùÒÔÕâÀïÎÒÃǰÑËùÓеĴúÂë¶¼·ÅÔÚwebdemo/api/controllers/v1/users.pyÎļþÖС£À´¿´ÏºÍuserÀàÐͶ¨ÒåÏà¹ØµÄ²¿·Ö£º

from wsme import types as wtypes


class User(wtypes.Base):
name = wtypes.text
age = int


class Users(wtypes.Base):
users = [User]

ÕâÀïÎÒÃǶ¨ÒåÁËclass User£¬±íʾһ¸öÓû§ÐÅÏ¢£¬°üº¬Á½¸ö×ֶΣ¬nameÊÇÒ»¸öÎı¾£¬ageÊÇÒ»¸öÕûÐÍ¡£class Users±íʾһ×éÓû§ÐÅÏ¢£¬°üº¬Ò»¸ö×Ö¶Îusers£¬ÊÇÒ»¸öÁÐ±í£¬ÁбíµÄÔªËØÊÇÉÏÃæ¶¨ÒåµÄclass User¡£Íê³ÉÕâЩ¶¨Òåºó£¬ÎÒÃǾÍʹÓÃWSMEÀ´¼ì²éÎÒÃǵÄAPIÊÇ·ñ·µ»ØÁ˺ϸñµÄÖµ£»ÁíÒ»·½Ã棬ֻҪÎÒÃǵÄAPI·µ»ØÁËÕâЩÀàÐÍ£¬ÄÇô¾ÍÄÜͨ¹ýWSMEµÄ¼ì²é¡£ÎÒÃÇÏÈÀ´Íê³ÉÀûÓÃWSMEÀ´¼ì²éAPI·µ»ØÖµµÄ´úÂ룺

class UsersController(rest.RestController):

# expose·½·¨µÄµÚÒ»¸ö²ÎÊý±íʾ·µ»ØÖµµÄÀàÐÍ
@expose.expose(Users)
def get(self):
pass

ÕâÑù¾ÍÍê³ÉÁËAPIµÄ·µ»ØÖµ¼ì²éÁË¡£

ʵÏÖAPIÂß¼­

ÎÒÃÇÏÖÔÚÀ´Íê³ÉAPIµÄÂß¼­²¿·Ö¡£²»¹ýΪÁË·½±ã´ó¼ÒÀí½â£¬ÎÒÃÇÖ±½Ó·µ»ØÒ»¸öдºÃµÄÊý¾Ý£¬¾ÍÊÇÉÏÃæÌù³öÀ´µÄÄǸö¡£

class UsersController(rest.RestController):

@expose.expose(Users)
def get(self):
user_info_list = [
{
'name': 'Alice',
'age': 30,
},
{
'name': 'Bob',
'age': 40,
}
]
users_list = [User(**user_info) for user_info in user_info_list]
return Users(users=users_list)

´úÂëÖУ¬»áÏȸù¾ÝuserÐÅÏ¢Éú³ÉUserʵÀýµÄÁбíusers_list£¬È»ºóÔÙÉú³ÉUsersʵÀý¡£´Ëʱ£¬ÖØÆô²âÊÔ·þÎñÆ÷ºó£¬Äã¾Í¿ÉÒÔ´Óä¯ÀÀÆ÷·ÃÎÊhttp://localhost:8080/v1/users£¬¾ÍÄÜ¿´µ½½á¹ûÁË¡£

ʵÏÖ POST /v1/users

Õâ¸öAPI»á½ÓÊÕÓû§ÉÏ´«µÄÒ»¸öJSON¸ñʽµÄÊý¾Ý£¬È»ºó´òÓ¡³öÀ´£¨Êµ¼ÊÖÐÒ»°ãÊÇ´æµ½Êý¾Ý¿âÖ®ÀàµÄ£©£¬ÒªÇóÓû§ÉÏ´«µÄÊý¾Ý·ûºÏUserÀàÐ͵Ĺ淶£¬²¢ÇÒ·µ»ØµÄ״̬ÂëΪ201¡£´úÂëÈçÏ£º

class UsersController(rest.RestController):

@expose.expose(None, body=User, status_code=201)
def post(self, user):
print user

¿ÉÒÔʹÓÃcurl³ÌÐòÀ´²âÊÔ£º

~/programming/python/webdemo git:(master) -> $ curl -X POST 
http://localhost:8080/v1/users -H "Content-Type: application/json" -d
'{"name": "Cook", "age": 50}' -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /v1/users HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 27
>
* upload completely sent off: 27 out of 27 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 201 Created
< Date: Mon, 16 Nov 2015 15:18:24 GMT
< Server: WSGIServer/0.1 Python/2.7.10
< Content-Length: 0
<
* Closing connection 0

ͬʱ£¬·þÎñÆ÷ÉÏÒ²»á´òÓ¡³ö£º

127.0.0.1 - - [16/Nov/2015 23:16:28] "POST /v1/users HTTP/1.1" 201 0
<webdemo.api.controllers.v1.users.User object at 0x7f65e058d550>

ÎÒÃÇÓÃ3ÐдúÂë¾ÍʵÏÖÁËÕâ¸öPOSTµÄÂß¼­¡£ÏÖÔÚÀ´ËµÃ÷Ò»ÏÂÕâÀïµÄÃØÃÜ¡£expose×°ÊÎÆ÷µÄµÚÒ»¸ö²ÎÊý±íʾÕâ¸ö·½·¨Ã»Óзµ»ØÖµ£»µÚÈý¸ö²ÎÊý±íʾÕâ¸öAPIµÄÏìӦ״̬ÂëÊÇ201£¬Èç¹û²»¼ÓÕâ¸ö²ÎÊý£¬ÔÚûÓзµ»ØÖµµÄÇé¿öÏ£¬Ä¬Èϻ᷵»Ø204¡£µÚ¶þ¸ö²ÎÊýҪ˵Ã÷һϣ¬ÕâÀïÓõÄÊÇbody=User£¬ÄãÒ²¿ÉÒÔÖ±½ÓдUser¡£Ê¹ÓÃbody=UserÕâÖÖÐÎʽ£¬Äã¿ÉÒÔÖ±½Ó·¢ËÍ·ûºÏUser¹æ·¶µÄJSON×Ö·û´®£»Èç¹ûÊÇÓÃexpose(None, User, status_code=201)ÄÇôÄãÐèÒª·¢ËÍÏÂÃæÕâÑùµÄÊý¾Ý£º

{ "user": {"name": "Cook", "age": 50} }

Äã¿ÉÒÔ×Ô¼º²âÊÔÒ»ÏÂÇø±ð¡£Òª¸ü¶àµÄÁ˽Ȿ½ÚÌáµ½µÄexpose²ÎÊý£¬Çë²Î¿¼WSMÎĵµFunctions¡£

×îºó£¬Äã½ÓÊÕµ½Ò»¸ö´´½¨Óû§ÇëÇóʱ£¬Ò»°ã»áΪÕâ¸öÓû§·ÖÅäÒ»¸öid¡£±¾ÎÄÇ°ÃæÒѾ­Ìáµ½ÁËOpenStackÏîÄ¿ÖÐÒ»°ãʹÓÃUUID¡£Äã¿ÉÒÔÐÞ¸ÄÒ»ÏÂÉÏÃæµÄÂß¼­£¬ÎªÃ¿¸öÓû§·ÖÅäÒ»¸öUUID¡£

ʵÏÖ GET /v1/users/

ҪʵÏÖÕâ¸öAPI£¬ÐèÒªÁ½¸ö²½Ö裺

1¡£ÔÚUsersControllerÖнâÎö³öµÄ²¿·Ö£¬È»ºó°ÑÇëÇ󴫵ݸøÕâ¸öÒ»¸öеÄUserController¡£´ÓÃüÃû¿ÉÒÔ¿´³ö£¬UsersControllerÊÇÕë¶Ô¶à¸öÓû§µÄ£¬UserControllerÊÇÕë¶ÔÒ»¸öÓû§µÄ¡£

2.ÔÚUserControllerÖÐʵÏÖget()·½·¨¡£

ʹÓÃ_lookup()·½·¨

PecanµÄ_lookup()·½·¨ÊÇcontrollerÖеÄÒ»¸öÌØÊâ·½·¨£¬Pecan»áÔÚÌØ¶¨µÄʱºòµ÷ÓÃÕâ¸ö·½·¨À´ÊµÏÖ¸üÁé»îµÄURL·ÓÉ¡£Pecan»¹Ö§³ÖÓû§ÊµÏÖ_default()ºÍ_route()·½·¨¡£ÕâЩ·½·¨µÄ¾ßÌå˵Ã÷£¬ÇëÔĶÁPecanµÄÎĵµ£ºrouting¡£

ÎÒÃÇÕâÀïÖ»Óõ½_lookup()·½·¨£¬Õâ¸ö·½·¨»áÔÚcontrollerÖÐûÓÐÆäËû·½·¨¿ÉÒÔÖ´ÐÐÇÒûÓÐ_default()·½·¨µÄʱºòÖ´ÐС£±ÈÈçÉÏÃæµÄUsersControllerÖУ¬Ã»Óж¨Òå/v1/users/ÈçºÎ´¦Àí£¬ËüÖ»ÄÜ·µ»Ø404£»Èç¹ûÄ㶨ÒåÁË_lookup()·½·¨£¬ÄÇôËü¾Í»áµ÷Óø÷½·¨¡£

_lookup()·½·¨ÐèÒª·µ»ØÒ»¸öÔª×飬Ԫ×éµÄµÚÒ»¸öÔªËØÊÇÏÂÒ»¸öcontrollerµÄʵÀý£¬µÚ¶þ¸öÔªËØÊÇURL pathÖÐÊ£ÓàµÄ²¿·Ö¡£

ÔÚÕâÀÎÒÃǾÍÐèÒªÔÚ_lookup()·½·¨ÖнâÎö³öUUIDµÄ²¿·Ö²¢´«µÝ¸øÐµÄcontroller×÷ΪеIJÎÊý£¬²¢ÇÒ·µ»ØÊ£ÓàµÄURL path¡£À´¿´Ï´úÂ룺

class UserController(rest.RestController):

def __init__(self, user_id):
self.user_id = user_id


class UsersController(rest.RestController):

@pecan.expose()
def _lookup(self, user_id, *remainder):
return UserController(user_id), remainder

_lookup()·½·¨µÄÐÎʽΪ_lookup(self, user_id, *remainder)£¬Òâ˼¾ÍÊÇ»á°Ñ/v1/users/ÖеIJ¿·Ö×÷Ϊuser_idÕâ¸ö²ÎÊý£¬Ê£ÓàµÄ°´ÕÕ¡±/¡±·Ö¸îΪһ¸öÊý×é²ÎÊý£¨ÕâÀïremainderΪ¿Õ£©¡£È»ºó£¬_lookup()·½·¨Àï»á³õʼ»¯Ò»¸öUserControllerʵÀý£¬Ê¹ÓÃuser_id×÷Ϊ³õʼ»¯²ÎÊý¡£Õâô×öÖ®ºó£¬Õâ¸ö³õʼ»¯µÄ¿ØÖÆÆ÷¾ÍÄÜÖªµÀÊÇÒª²éÕÒÄĸöÓû§ÁË¡£È»ºóÕâ¸ö¿ØÖÆÆ÷»á±»·µ»Ø£¬×÷ΪÏÂÒ»¸ö¿ØÖƱ»µ÷Óá£ÇëÇóµÄ´¦ÀíÁ÷³Ì¾ÍÕâÃ´×ªÒÆµ½UserControllerÖÐÁË¡£

ʵÏÖAPIÂß¼­

ʵÏÖǰ£¬ÎÒÃÇÒªÏÈÐÞ¸ÄÒ»ÏÂÎÒÃÇ·µ»ØµÄÊý¾Ý£¬ÀïÃæÐèÒªÔö¼ÓÒ»¸öid×ֶΡ£¶ÔÓ¦µÄUser¶¨ÒåÈçÏ£º

class User(wtypes.Base):
id = wtypes.text
name = wtypes.text
age = int

ÏÖÔÚ£¬ÍêÕûµÄUserController´úÂëÈçÏ£º

class UserController(rest.RestController):

def __init__(self, user_id):
self.user_id = user_id

@expose.expose(User)
def get(self):
user_info = {
'id': self.user_id,
'name': 'Alice',
'age': 30,
}
return User(**user_info)

ʹÓÃcurlÀ´¼ì²éÒ»ÏÂЧ¹û£º

~/programming/python/webdemo git:(master) ? $ curl 
http://localhost:8080/v1/users/29520c88de6b4c76ae8deb48db0a71e7
{"age": 30, "id": "29520c88de6b4c76ae8deb48db0a71e7", "name": "Alice"}

¶¨ÒåWSMEÀàÐ͵ļ¼ÇÉ

Äã¿ÉÄÜ»áÓÐÒÉÎÊ£ºÕâÀïÎÒÃÇÐÞ¸ÄÁËUserÀàÐÍ£¬Ôö¼ÓÁËÒ»¸öid×ֶΣ¬ÄÇÃ´Ç°ÃæÊµÏÖµÄPOST /v1/users»á²»»áÊ§Ð§ÄØ£¿Äã¿ÉÒÔ×Ô¼º²âÊÔһϡ££¨´ð°¸ÊDz»»á£¬ÒòΪÕâ¸öÀàÐÍÀïµÄ×ֶζ¼ÊÇ¿ÉÑ¡µÄ£©¡£ÕâÀï˳±ã½²Á½¸ö¼¼ÇÉ¡£

ÈçºÎÉèÖÃÒ»¸ö×Ö¶ÎÎªÇ¿ÖÆ×Ö¶Î

ÏñÏÂÃæÕâÑù×ö¾Í¿ÉÒÔÁË£¨Äã¿ÉÒÔ²âÊÔһϣ¬¸Ä³ÉÕâÑùºó£¬²»´«µÝidµÄPOST /v1/users»áʧ°Ü£©£º

class User(wtypes.Base):
id = wtypes.wsattr(wtypes.text, mandatory=True)
name = wtypes.text
age = int

ÈçºÎ¼ì²éÒ»¸ö¿ÉÑ¡×ֶεÄÖµÊÇ·ñ´æÔÚ

¼ì²éÕâ¸öÖµÊÇ·ñΪNoneÊǿ϶¨²»Ðеģ¬ÐèÒª¼ì²éÕâ¸öÖµÊÇ·ñΪwsme.Unset¡£

ʵÏÖ PUT /v1/users/Õâ¸öºÍÉÏÒ»¸öAPIÒ»Ñù£¬²»¹ý_lookup()·½·¨ÒѾ­ÊµÏÖ¹ýÁË£¬Ö±½ÓÌí¼Ó·½·¨µ½UserControllerÖм´¿É£º

class UserController(rest.RestController):

@expose.expose(User, body=User)
def put(self, user):
user_info = {
'id': self.user_id,
'name': user.name,
'age': user.age + 1,
}
return User(**user_info)

ͨ¹ýcurlÀ´²âÊÔ£º

-> ~/programming/python/webdemo git:(master) ? $ curl -X PUT
http://localhost:8080/v1/users/29520c88de6b4c76ae8deb48db0a71e7 -H
"Content-Type: application/json" -d '{"name": "Cook", "age": 50}'
{"age": 51, "id": "29520c88de6b4c76ae8deb48db0a71e7", "name": "Cook"}%

ʵÏÖ DELETE /v1/users/

ͬÉÏ£¬Ã»ÓÐʲôеÄÄÚÈÝ£º

class UserController(rest.RestController):

@expose.expose()
def delete(self):
print 'Delete user_id: %s' % self.user_id

×ܽá

µ½´ËΪֹ£¬ÎÒÃÇÒѾ­Íê³ÉÁËÎÒÃǵÄAPI·þÎñÁË£¬ËäȻûÓÐʵ¼ÊµÄÂß¼­£¬µ«ÊDZ¾ÎĴÆðÀ´µÄ¿ò¼ÜÒ²ÊÇOpenStackÖÐAPI·þÎñµÄÒ»¸ö³£Óÿò¼Ü£¬ºÜ¶à´óÏîÄ¿µÄAPI·þÎñ´úÂë¶¼ºÍÎÒÃǵÄwebdemo³¤µÃ²î²»¶à¡£×îºóÔÙ˵һÏ£¬±¾ÎĵĴúÂëÔÚgithubÉÏÍйÜ×Å£ºdiabloneo/webdemo¡£

ÏÖÔÚÎÒÃÇÒѾ­Á˽âÁ˰ü¹ÜÀíºÍAPI·þÎñÁË£¬ÄÇô½ÓÏÂÀ´¾ÍÒª¿ªÊ¼Êý¾Ý¿âÏà¹ØµÄ²Ù×÷ÁË¡£´ó²¿·ÖOpenStackµÄÏîÄ¿¶¼ÊÇʹÓ÷dz£ÖøÃûµÄsqlalchemy¿âÀ´ÊµÏÖÊý¾Ý¿â²Ù×÷µÄ£¬±¾ÏµÁнÓÏÂÀ´µÄÎÄÕ¾ÍÊÇÒªÀ´ËµÃ÷Êý¾Ý¿âµÄÏà¹ØÖªÊ¶ºÍÓ¦Óá£

   
3619 ´Îä¯ÀÀ       28
 
Ïà¹ØÎÄÕÂ

ÔÆ¼ÆËãµÄ¼Ü¹¹
¶ÔÔÆ¼ÆËã·þÎñÄ£ÐÍ
ÔÆ¼ÆËãºËÐļ¼ÊõÆÊÎö
Á˽âÔÆ¼ÆËãµÄ©¶´
 
Ïà¹ØÎĵµ

ÔÆ¼ÆËã¼ò½é
ÔÆ¼ÆËã¼ò½éÓëÔÆ°²È«
ÏÂÒ»´úÍøÂç¼ÆËã--ÔÆ¼ÆËã
ÈídzÎöÔÆ¼ÆËã
 
Ïà¹Ø¿Î³Ì

ÔÆ¼ÆËãÔ­ÀíÓëÓ¦ÓÃ
ÔÆ¼ÆËãÓ¦ÓÃÓ뿪·¢
CMMIÌåϵÓëʵ¼ù
»ùÓÚCMMI±ê×¼µÄÈí¼þÖÊÁ¿±£Ö¤
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

ר¼ÒÊӽǿ´ITÓë¼Ü¹¹
Èí¼þ¼Ü¹¹Éè¼Æ
ÃæÏò·þÎñÌåϵ¼Ü¹¹ºÍÒµÎñ×é¼þµÄ˼¿¼
ÈËÈËÍøÒÆ¶¯¿ª·¢¼Ü¹¹
¼Ü¹¹¸¯»¯Ö®ÃÕ
̸ƽ̨¼´·þÎñPaaS
Ïà¹ØÅàѵ¿Î³Ì

ÔÆ¼ÆËãÔ­ÀíÓëÓ¦ÓÃ
Windows Azure ÔÆ¼ÆËãÓ¦ÓÃ

ĦÍÐÂÞÀ­ ÔÆÆ½Ì¨µÄ¹¹½¨ÓëÓ¦ÓÃ
ͨÓù«Ë¾GE DockerÔ­ÀíÓëʵ¼ù
ijÑз¢ÖÐÐÄ Openstackʵ¼ù
ÖªÃûµç×Ó¹«Ë¾ ÔÆÆ½Ì¨¼Ü¹¹ÓëÓ¦ÓÃ
ijµçÁ¦ÐÐÒµ »ùÓÚÔÆÆ½Ì¨¹¹½¨ÔÆ·þÎñ
ÔÆ¼ÆËãÓëWindows AzureÅàѵ
±±¾© ÔÆ¼ÆËãÔ­ÀíÓëÓ¦ÓÃ