摘要:從上面的例子可以看出,決定響應(yīng)類型的主要是傳遞給函數(shù)的參數(shù),我們看下函數(shù)的完整聲明參數(shù)用來指定返回值的模板,如果是就會(huì)返回內(nèi)容,這里可以指定一個(gè)文件,或者指定一個(gè)模板。用來做什么上面兩節(jié)已經(jīng)說明了可以比較好的處理請(qǐng)求中的參數(shù)以及控制返回值。
上一篇文章我們了解了一個(gè)巨啰嗦的框架:Paste + PasteDeploy + Routes + WebOb。后來OpenStack社區(qū)的人受不了這么啰嗦的代碼了,決定換一個(gè)框架,他們最終選中了Pecan。Pecan框架相比上一篇文章的啰嗦框架有如下好處:
不用自己寫WSGI application了
請(qǐng)求路由很容易就可以實(shí)現(xiàn)了
總的來說,用上Pecan框架以后,很多重復(fù)的代碼不用寫了,開發(fā)人員可以專注于業(yè)務(wù),也就是實(shí)現(xiàn)每個(gè)API的功能。
PecanPecan框架的目標(biāo)是實(shí)現(xiàn)一個(gè)采用對(duì)象分發(fā)方式進(jìn)行URL路由的輕量級(jí)Web框架。它非常專注于自己的目標(biāo),它的大部分功能都和URL路由以及請(qǐng)求和響應(yīng)的處理相關(guān),而不去實(shí)現(xiàn)模板、安全以及數(shù)據(jù)庫(kù)層,這些東西都可以通過其他的庫(kù)來實(shí)現(xiàn)。關(guān)于Pecan的更多信息,可以查看文檔:https://pecan.readthedocs.org/en/latest/index.html。本文以O(shè)penStack的magnum項(xiàng)目為例來說明Pecan項(xiàng)目在實(shí)際中的應(yīng)用,但是本文不會(huì)詳細(xì)講解Pecan的各個(gè)方面,一些細(xì)節(jié)請(qǐng)讀者閱讀Pecan的文檔。
項(xiàng)目中的代碼結(jié)構(gòu)使用Pecan框架時(shí),OpenStack項(xiàng)目一般會(huì)把API服務(wù)的實(shí)現(xiàn)都放在一個(gè)api目錄下,比如magnum項(xiàng)目是這樣的:
? ~/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項(xiàng)目中看到類似的結(jié)構(gòu)。介紹一下幾個(gè)主要的文件,這樣你以后看到一個(gè)使用Pecan的OpenStack項(xiàng)目時(shí)就會(huì)比較容易找到入口。
app.py 一般包含了Pecan應(yīng)用的入口,包含應(yīng)用初始化代碼
config.py 包含Pecan的應(yīng)用配置,會(huì)被app.py使用
controllers/ 這個(gè)目錄會(huì)包含所有的控制器,也就是API具體邏輯的地方
controllers/root.py 這個(gè)包含根路徑對(duì)應(yīng)的控制器
controllers/v1/ 這個(gè)目錄對(duì)應(yīng)v1版本的API的控制器。如果有多個(gè)版本的API,你一般能看到v2等目錄。
代碼變少了:application的配置Pecan的配置很容易,通過一個(gè)Python源碼式的配置文件就可以完成基本的配置。這個(gè)配置的主要目的是指定應(yīng)用程序的root,然后用于生成WSGI application。我們來看Magnum項(xiàng)目的例子。Magnum項(xiàng)目有個(gè)API服務(wù)是用Pecan實(shí)現(xiàn)的,在magnum/api/config.py文件中可以找到這個(gè)文件,主要內(nèi)容如下:
app = { "root": "magnum.api.controllers.root.RootController", "modules": ["magnum.api"], "debug": False, "hooks": [ hooks.ContextHook(), hooks.RPCHook(), hooks.NoExceptionTracebackHook(), ], "acl_public_routes": [ "/" ], }
上面這個(gè)app對(duì)象就是Pecan的配置,每個(gè)Pecan應(yīng)用都需要有這么一個(gè)名為app的配置。app配置中最主要的就是root的值,這個(gè)值表示了應(yīng)用程序的入口,也就是從哪個(gè)地方開始解析HTTP的根path:/。hooks對(duì)應(yīng)的配置是一些Pecan的hook,作用類似于WSGI Middleware。
有了app配置后,就可以讓Pecan生成一個(gè)WSGI application。在Magnum項(xiàng)目中,magnum/api/app.py文件就是生成WSGI application的地方,我們來看一下這個(gè)的主要內(nèi)容:
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文件,然后返回一個(gè)pecan.configuration.Config對(duì)象。setup_app()函數(shù)首先調(diào)用get_pecan_config()函數(shù)獲取application的配置,然后調(diào)用pecan.make_app()函數(shù)創(chuàng)建了一個(gè)WSGI application,最后調(diào)用了 auth.install()函數(shù)(也就是magnum.api.auth.install()函數(shù))為剛剛生成的WSGI application加上Keystone的認(rèn)證中間件(確保所有的請(qǐng)求都會(huì)通過Keystone認(rèn)證)。
到這邊為止,一個(gè)Pecan的WSGI application就已經(jīng)準(zhǔn)備好了,只要調(diào)用這個(gè)setup_app()函數(shù)就能獲得。至于如何部署這個(gè)WSGI application,請(qǐng)參考WSGI簡(jiǎn)介這篇文章。
從Magnum這個(gè)實(shí)際的例子可以看出,使用了Pecan之后,我們不再需要自己寫那些冗余的WSGI application代碼了,直接調(diào)用Pecan的make_app()函數(shù)就能完成這些工作。另外,對(duì)于之前使用PasteDeploy時(shí)用到的很多WSGI中間件,可以選擇使用Pecan的hooks機(jī)制來實(shí)現(xiàn),也選擇使用WSGI中間件的方式來實(shí)現(xiàn)。在Magnum的API服務(wù)就同時(shí)使用了這兩種方式。其實(shí),Pecan還可以和PasteDeploy一起使用,Ceilometer項(xiàng)目就是這么做的,大家可以看看。
確定路由變得容易了:對(duì)象分發(fā)式的路由Pecan不僅縮減了生成WSGI application的代碼,而且也讓開發(fā)人員更容易的指定一個(gè)application的路由。Pecan采用了一種對(duì)象分發(fā)風(fēng)格(object-dispatch style)的路由模式。我們直接通過例子來解釋這種路由模式,還是以Magnum項(xiàng)目為例。
上面提到了,Magnum的API服務(wù)的root是magnum.api.controllers.root.RootController。這里的RootController的是一個(gè)類,我們來看它的代碼:
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): """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)
別看這個(gè)類這么長(zhǎng),我來解釋一下你就懂了。首先,你可以先忽略掉_route()函數(shù),這個(gè)函數(shù)是用來覆蓋Pecan的默認(rèn)路由實(shí)現(xiàn)的,在這里去掉它不妨礙我們理解Pecan(這里的_route()函數(shù)的作用把所有請(qǐng)求重定向到默認(rèn)的API版本去)。去掉_route()和其他的東西后,整個(gè)類就變成這么短:
class RootController(rest.RestController): v1 = v1.Controller() @expose.expose(Root) def get(self): return Root.convert()
首先,你要記住,這個(gè)RootController對(duì)應(yīng)的是URL中根路徑,也就是path中最左邊的/。
RootController繼承自rest.RestController,是Pecan實(shí)現(xiàn)的RESTful控制器。這里的get()函數(shù)表示,當(dāng)訪問的是GET /時(shí),由該函數(shù)處理。get()函數(shù)會(huì)返回一個(gè)WSME對(duì)象,表示一個(gè)形式化的HTTP Response,這個(gè)下面再講。get()函數(shù)上面的expose裝飾器是Pecan實(shí)現(xiàn)路由控制的一個(gè)方式,被expose的函數(shù)才會(huì)被路由處理。
這里的v1 = v1.Controller()表示,當(dāng)訪問的是GET /v1或者GET /v1/...時(shí),請(qǐng)求由一個(gè)v1.Controller實(shí)例來處理。
為了加深大家的理解,我們?cè)賮砜聪?b>v1.Controller的實(shí)現(xiàn):
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() ...
上面這個(gè)Controller也是繼承自rest.RestController。所以它的get函數(shù)表示,當(dāng)訪問的是GET /v1的時(shí)候,要做的處理。然后,它還有很多類屬性,這些屬性分別表示不同URL路徑的控制器:
/v1/bays 由bays處理
/v1/baymodels 由baymodels處理
/v1/containers 由containers處理
其他的都是類似的。我們?cè)倮^續(xù)看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(...):
這個(gè)controller中只有函數(shù),沒有任何類屬性,而且沒有實(shí)現(xiàn)任何特殊方法,所以/v1/bays開頭的URL處理都在這個(gè)controller中終結(jié)。這個(gè)類會(huì)處理如下請(qǐng)求:
GET /v1/bays
GET /v1/bays/{UUID}
POST /v1/bays
PATCH /v1/bays/{UUID}
DELETE /v1/bays/{UUID}
GET /v1/bays/detail/{UUID}
看了上面的3個(gè)controller之后,你應(yīng)該能大概明白Pecan是如何對(duì)URL進(jìn)行路由的。這種路由方式就是對(duì)象分發(fā):根據(jù)類屬性,包括數(shù)據(jù)屬性和方法屬性來決定如何路由一個(gè)HTTP請(qǐng)求。Pecan的文檔中對(duì)請(qǐng)求的路由有專門的描述,要想掌握Pecan的路由還是要完整的看一下官方文檔。
內(nèi)置RESTful支持我們上面舉例的controller都是繼承自pecan.rest.RestController,這種controller稱為RESTful controller,專門用于實(shí)現(xiàn)RESTful API的,因此在OpenStack中使用特別多。Pecan還支持普通的controller,稱為Generic controller。Generic controller繼承自object對(duì)象,默認(rèn)沒有實(shí)現(xiàn)對(duì)RESTful請(qǐng)求的方法。簡(jiǎn)單的說,RESTful controller幫我們規(guī)定好了get_one(), get_all(), get(), post()等方法對(duì)應(yīng)的HTTP請(qǐng)求,而Generic controller則沒有。關(guān)于這兩種controller的區(qū)別,可以看官方文檔Writing RESTful Web Services with Generic Controllers,有很清楚的示例。
對(duì)于RestController中沒有預(yù)先定義好的方法,我們可以通過控制器的_custom_actions屬性來指定其能處理的方法。
class RootController(rest.RestController): _custom_actions = { "test": ["GET"], } @expose() def test(self): return "hello"
上面這個(gè)控制器是一個(gè)根控制器,指定了/test路徑支持GET方法,效果如下:
$ curl http://localhost:8080/test hello%那么HTTP請(qǐng)求和HTTP響應(yīng)呢?
上面講了這么多,我們都沒有說明在Pecan中如何處理請(qǐng)求和如何返回響應(yīng)。這個(gè)將在下一章中說明,同時(shí)我們會(huì)引入一個(gè)新的庫(kù)WSME。
WSME Pecan對(duì)請(qǐng)求和響應(yīng)的處理在開始提到WSME之前,我們先來看下Pecan自己對(duì)HTTP請(qǐng)求和響應(yīng)的處理。這樣你能更好的理解為什么會(huì)再引入一個(gè)WSME庫(kù)。
Pecan框架為每個(gè)線程維護(hù)了多帶帶的請(qǐng)求和響應(yīng)對(duì)象,你可以直接在請(qǐng)求處理函數(shù)中訪問。pecan.request和pecan.response分別代表當(dāng)前需要處理的請(qǐng)求和響應(yīng)對(duì)象。你可以直接操作這兩個(gè)對(duì)象,比如指定響應(yīng)的狀態(tài)碼,就像下面這個(gè)例子一樣(例子來自官方文檔):
@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!"
這個(gè)例子演示了訪問POST請(qǐng)求的參數(shù)以及返回403。你也可以重新構(gòu)造一個(gè)pecan.Response對(duì)象作為返回值(例子來自官方文檔):
from pecan import expose, Response class RootController(object): @expose() def hello(self): return Response("Hello, World!", 202)
另外,HTTP請(qǐng)求的參數(shù)也會(huì)可以作為控制器方法的參數(shù),還是來看幾個(gè)官方文檔的例子:
class RootController(object): @expose() def index(self, arg): return arg @expose() def kwargs(self, **kwargs): return str(kwargs)
這個(gè)控制器中的方法直接返回了參數(shù),演示了對(duì)GET請(qǐng)求參數(shù)的處理,效果是這樣的:
$ 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"}
有時(shí)候,參數(shù)也可能是URL的一部分,比如最后的一段path作為參數(shù),就像下面這樣:
class RootController(object): @expose() def args(self, *args): return ",".join(args)
效果是這樣的:
$ curl http://localhost:8080/args/one/two/three one,two,three
另外,我們還要看一下POST方法的參數(shù)如何處理(例子來自官方文檔):
class RootController(object): @expose() def index(self, arg): return arg
效果如下,就是把HTTP body解析成了控制器方法的參數(shù):
$ curl -X POST "http://localhost:8080/" -H "Content-Type: application/x-www-form-urlencoded" -d "arg=foo" foo返回JSON還是HTML?
如果你不是明確的返回一個(gè)Response對(duì)象,那么Pecan中方法的返回內(nèi)容類型就是由expose()裝飾器決定的。默認(rèn)情況下,控制器的方法返回的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"}%
甚至,你還可以讓一個(gè)控制器方法根據(jù)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"}%
這里要注意一下:
同一個(gè)字符串作為JSON返回和作為HTML返回是不一樣的,仔細(xì)看一下HTTP響應(yīng)的內(nèi)容。
我們的例子中在URL的最后加上了.html后綴或者.json后綴,請(qǐng)嘗試一下不加后綴的化是返回什么?然后,調(diào)換一下兩個(gè)expose()的順序再試一下。
從上面的例子可以看出,決定響應(yīng)類型的主要是傳遞給expose()函數(shù)的參數(shù),我們看下expose()函數(shù)的完整聲明:
pecan.decorators.expose(template=None, content_type="text/html", generic=False)
template參數(shù)用來指定返回值的模板,如果是"json"就會(huì)返回JSON內(nèi)容,這里可以指定一個(gè)HTML文件,或者指定一個(gè)mako模板。
content_type指定響應(yīng)的content-type,默認(rèn)值是"text/html"。
generic參數(shù)表明該方法是一個(gè)“泛型”方法,可以指定多個(gè)不同的函數(shù)對(duì)應(yīng)同一個(gè)路徑的不同的HTTP方法。
看過參數(shù)的解釋后,你應(yīng)該能大概了解expose()函數(shù)是如何控制HTTP響應(yīng)的內(nèi)容和類型的。
用WSME來做什么?上面兩節(jié)已經(jīng)說明了Pecan可以比較好的處理HTTP請(qǐng)求中的參數(shù)以及控制HTTP返回值。那么為什么我們還需要WSME呢?因?yàn)镻ecan在做下面這個(gè)事情的時(shí)候比較麻煩:請(qǐng)求參數(shù)和響應(yīng)內(nèi)容的類型檢查(英文簡(jiǎn)稱就是typing)。當(dāng)然,做是可以做的,不過你需要自己訪問pecan.request和pecan.response,然后檢查指定的值的類型。WSME就是為解決這個(gè)問題而生的,而且適用場(chǎng)景就是RESTful API。
WSME簡(jiǎn)介WSME的全稱是Web Service Made Easy,是專門用于實(shí)現(xiàn)REST服務(wù)的typing庫(kù),讓你不需要直接操作請(qǐng)求和響應(yīng),而且剛好和Pecan結(jié)合得非常好,所以O(shè)penStack的很多項(xiàng)目都使用了Pecan + WSME的組合來實(shí)現(xiàn)API(好吧,我看過的項(xiàng)目,用了Pecan的都用了WSME)。WSME的理念是:在大部分情況下,Web服務(wù)的輸入和輸出對(duì)數(shù)據(jù)類型的要求都是嚴(yán)格的。所以它就專門解決了這個(gè)事情,然后把其他事情都交給其他框架去實(shí)現(xiàn)。因此,一般WSME都是和其他框架配合使用的,支持Pecan、Flask等。WSME的文檔地址是http://wsme.readthedocs.org/en/latest/index.html。
WSME的使用用了WSME后的好處是什么呢?WSME會(huì)自動(dòng)幫你檢查HTTP請(qǐng)求和響應(yīng)中的數(shù)據(jù)是否符合預(yù)先設(shè)定好的要求。WSME的主要方式是通過裝飾器來控制controller方法的輸入和輸出。WSME中主要使用兩個(gè)控制器:
@signature: 這個(gè)裝飾器用來描述一個(gè)函數(shù)的輸入和輸出。
@wsexpose: 這個(gè)裝飾器包含@signature的功能,同時(shí)會(huì)把函數(shù)的路由信息暴露給Web框架,效果就像Pecan的expose裝飾器。
這里我們結(jié)合Pecan來講解WSME的使用。先來看一個(gè)原始類型的例子:
from wsmeext.pecan import wsexpose class RootController(rest.RestController): _custom_actions = { "test": ["GET"], } @wsexpose(int, int) def test(self, number): return number
如果不提供參數(shù),訪問會(huì)失敗:
$ curl http://localhost:8080/test {"debuginfo": null, "faultcode": "Client", "faultstring": "Missing argument: "number""}%
如果提供的參數(shù)不是整型,訪問也會(huì)失敗:
$ 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"}%
上面這些錯(cuò)誤信息都是由WSME框架直接返回的,還沒有執(zhí)行到你寫的方法。如果請(qǐng)求正確,那么會(huì)是這樣的:
$ 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%
請(qǐng)注意返回的content-type,這里返回JSON是因?yàn)槲覀兪褂玫?b>wsexpose設(shè)置的返回類型是XML和JSON,并且JSON是默認(rèn)值。上面這個(gè)例子就是WSME最簡(jiǎn)單的應(yīng)用了。
那么現(xiàn)在有下面這些問題需要思考一下:
如果想用POST的方式來傳遞參數(shù),要怎么做呢?提示:要閱讀WSME中@signature裝飾器的文檔。
如果我希望使用/test/1這種方式來傳遞參數(shù)要怎么做呢?提示:要閱讀Pecan文檔中關(guān)于路由的部分。
WSME中支持對(duì)哪些類型的檢查呢?WSME支持整型、浮點(diǎn)型、字符串、布爾型、日期時(shí)間等,甚至還支持用戶自定義類型。提示:要閱讀WSME文檔中關(guān)于類型的部分。
WSME支持?jǐn)?shù)組類型么?支持。
上面的問題其實(shí)也是很多人使用WSME的時(shí)候經(jīng)常問的問題。我們將在下一篇文章中使用Pecan + WSME來繼續(xù)開發(fā)我們的demo,并且用代碼來回答上面所有的問題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/37614.html
摘要:通過,也就是通過各個(gè)項(xiàng)目提供的來使用各個(gè)服務(wù)的功能。通過使用的方式是由各個(gè)服務(wù)自己實(shí)現(xiàn)的,比如負(fù)責(zé)計(jì)算的項(xiàng)目實(shí)現(xiàn)了計(jì)算相關(guān)的,負(fù)責(zé)認(rèn)證的項(xiàng)目實(shí)現(xiàn)了認(rèn)證和授權(quán)相關(guān)的。的服務(wù)都是使用的方式來部署的。 使用OpenStack服務(wù)的方式 OpenStack項(xiàng)目作為一個(gè)IaaS平臺(tái),提供了三種使用方式: 通過Web界面,也就是通過Dashboard(面板)來使用平臺(tái)上的功能。 通過命令行,也就...
摘要:在實(shí)際項(xiàng)目中,這么做肯定是不行的實(shí)際項(xiàng)目中不會(huì)使用內(nèi)存數(shù)據(jù)庫(kù),這種數(shù)據(jù)庫(kù)一般只是在單元測(cè)試中使用。接下來,我們將會(huì)了解中單元測(cè)試的相關(guān)知識(shí)。 在上一篇文章,我們介紹了SQLAlchemy的基本概念,也介紹了基本的使用流程。本文我們結(jié)合webdemo這個(gè)項(xiàng)目來介紹如何在項(xiàng)目中使用SQLAlchemy。另外,我們還會(huì)介紹數(shù)據(jù)庫(kù)版本管理的概念和實(shí)踐,這也是OpenStack每個(gè)項(xiàng)目都需要做的...
摘要:到這里,我們的服務(wù)的框架已經(jīng)搭建完成,并且測(cè)試服務(wù)器也跑起來了。上面的代碼也就可以修改為再次運(yùn)行我們的測(cè)試服務(wù)器,就可以返現(xiàn)返回值為格式了。我們先來完成利用來檢查返回值的代碼方法的第一個(gè)參數(shù)表示返回值的類型這樣就完成了的返回值檢查了。 上一篇文章說到,我們將以實(shí)例的形式來繼續(xù)講述這個(gè)API服務(wù)的開發(fā)知識(shí),這里會(huì)使用Pecan和WSME兩個(gè)庫(kù)。 設(shè)計(jì)REST API 要開發(fā)REST AP...
摘要:本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述和中的單元測(cè)試的生態(tài)環(huán)境。另外,在中指定要運(yùn)行的單元測(cè)試用例的完整語(yǔ)法是。中使用模塊管理單元測(cè)試用例。每個(gè)項(xiàng)目的單元測(cè)試代碼結(jié)構(gòu)可 本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述Python和OpenStack中的單元測(cè)試的生態(tài)環(huán)境。 單元測(cè)試的重要性 github上有個(gè)人畫了一些不同語(yǔ)言的學(xué)...
摘要:不幸的是,在軟件包管理十分混亂,至少歷史上十分混亂。的最大改進(jìn)是將函數(shù)的參數(shù)單獨(dú)放到一個(gè)的文件中這些成為包的元數(shù)據(jù)。基于的版本號(hào)管理。的版本推導(dǎo)這里重點(diǎn)說明一下基于的版本號(hào)管理這個(gè)功能。開發(fā)版本號(hào)的形式如下。 為什么寫這個(gè)系列 OpenStack是目前我所知的最大最復(fù)雜的基于Python項(xiàng)目。整個(gè)OpenStack項(xiàng)目包含了數(shù)十個(gè)主要的子項(xiàng)目,每個(gè)子項(xiàng)目所用到的庫(kù)也不盡相同。因此,對(duì)于...
閱讀 2016·2021-09-13 10:23
閱讀 2348·2021-09-02 09:47
閱讀 3806·2021-08-16 11:01
閱讀 1227·2021-07-25 21:37
閱讀 1609·2019-08-30 15:56
閱讀 543·2019-08-30 13:52
閱讀 3138·2019-08-26 10:17
閱讀 2455·2019-08-23 18:17