ÉÏһƪÎÄÕÂÎÒÃÇÁ˽âÁËÒ»¸ö¾Þ†ªàµĿò¼Ü£º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Õâ¸öÎļþ£¬°üº¬ÎÒÃÇÐèÒªµÄ°ü£º
È»ºó£¬ÎÒÃÇÊÖ¶¯´´½¨Ò»¸ö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¿âÀ´ÊµÏÖÊý¾Ý¿â²Ù×÷µÄ£¬±¾ÏµÁнÓÏÂÀ´µÄÎÄÕ¾ÍÊÇÒªÀ´ËµÃ÷Êý¾Ý¿âµÄÏà¹ØÖªÊ¶ºÍÓ¦Óá£
|