Nova API服務 之 處理HTTP請求的流程
一、回顧
1、Nova API服務的主要功能都在osapi_compute_app_v2應用程式中實現。osapi_compute_app_v2應用程式對應的工
廠方法是APIRouter類的factory方法。APIRouter類的factory方法會返回一個APIRouter物件。當客戶端傳送HTTP請
求的時候,會呼叫APIRouter物件的__call__方法。
2、APIRouter繼承自Router類,__call__方法便定義在Router類中。Router類的__call__方法的定義是self.router
對象,最終會返回資源對應的Controller物件。
3、servers資源的Controller物件是一個wsgi.Resource物件。因此。當客戶端傳送HTTP請求後,Nova API服務最終
會呼叫wsgi.Resource物件的__call__方法。 /nova/api/openstack/wsgi.py
def create_resource(ext_mgr):
return wsgi.Resource(Controller(ext_mgr))
二、wsgi.Resource類的初始化方法和__call__方法的定義
1、初始化方法
wsgi.Resource類的初始化方法定義如下:
Resource類是在資源的底層Controller類的基礎上,添加了序列化和反序列化的功能。def __init__(self, controller, action_peek = None, inherits = None, #底層Controller物件 self.controller = controller # 反序列化物件 default_deserializers = dict(json = JSONDeserializer) default_deserializers.update(deserializers) self.default_deserializers = default_deserializers # 序列化物件 self.default_serializers = dict(json = JSONDictSerializer) ......
序列化:是指將XML或JSON格式的資料轉化成字串格式,以便於在網路間傳輸
反序列化:將字串數格式的資料轉換為XML或JSON資料格式,以便於顯示和處理。 所以,在Resource類中通過兩個
成員變數default_deserializers和default_serializers來分別實現資料的反序列化和序列化。
2、__call__方法
Resource類的__call__方法定義如下:
可以看出,__call__方法最後呼叫了_process_stack方法。@webob.dec.wsgify(RequestClass=Request) //wsgify裝飾器 def __call__(self, request): # Identify the action, its arguments, and the requested # content type #獲取客戶端傳入的HTTP請求引數 action_args = self.get_action_args(request.environ) #獲取HTTP請求的操作名:post put delete get action = action_args.pop('action', None) #獲取客戶端傳入的報文 #content_type:客戶端傳入的報文格式 body:客戶端傳入的報文內容 content_type, body = self.get_body(request) #獲取伺服器返回的報文型別 accept = request.best_match_content_type() #處理HTTP請求 return self._process_stack(request, action, action_args, content_type, body, accept)
_process_stack方法的定義如下:
def _process_stack(self, request, action, action_args,
content_type, body, accept):
#獲取處理HTTP請求的方法
try:
meth, extensions = self.get_method(request, action,
content_type, body)
except (AttributeError, TypeError):
return Fault(webob.exc.HTTPNotFound())
except KeyError as ex:
msg = _("There is no such action: %s") % ex.args[0]
return Fault(webob.exc.HTTPBadRequest(explanation=msg))
except exception.MalformedRequestBody:
msg = _("Malformed request body")
return Fault(webob.exc.HTTPBadRequest(explanation=msg))
if body:
msg = _("Action: '%(action)s', calling method: %(meth)s, body: "
"%(body)s") % {'action': action,
'body': six.text_type(body, 'utf-8'),
'meth': str(meth)}
LOG.debug(strutils.mask_password(msg))
else:
LOG.debug("Calling method '%(meth)s'",
{'meth': str(meth)})
#反序列化客戶端傳入的訊息
try:
contents = {}
if self._should_have_body(request): #如果傳入客戶端訊息
# allow empty body with PUT and POST
if request.content_length == 0:
contents = {'body': None}
else:
contents = self.deserialize(meth, content_type, body)
except exception.InvalidContentType:
msg = _("Unsupported Content-Type")
return Fault(webob.exc.HTTPUnsupportedMediaType(explanation=msg))
except exception.MalformedRequestBody:
msg = _("Malformed request body")
return Fault(webob.exc.HTTPBadRequest(explanation=msg))
#更新請求的引數,將傳入的訊息體內容新增到action_args中
action_args.update(contents)
#獲取客戶端所屬的專案ID
project_id = action_args.pop("project_id", None)
context = request.environ.get('nova.context')
#檢查客戶端請求是否合法
if (context and project_id and (project_id != context.project_id)):
msg = _("Malformed request URL: URL's project_id '%(project_id)s'"
" doesn't match Context's project_id"
" '%(context_project_id)s'") % \
{'project_id': project_id,
'context_project_id': context.project_id}
return Fault(webob.exc.HTTPBadRequest(explanation=msg))
#執行HTTP請求的前向擴充套件方法
response, post = self.pre_process_extensions(extensions,
request, action_args)
#前向擴充套件方法沒有返回response,說明需要對請求進行進一步處理
if not response:
try:
with ResourceExceptionHandler():
#執行底層Controller物件中 處理HTTP請求 的方法
action_result = self.dispatch(meth, request, action_args)
except Fault as ex:
response = ex
#前向擴充套件方法沒有返回response,處理底層controller物件 方法返回的結果
if not response:
resp_obj = None
#如果controller物件方法返回結果為字典,則封裝成ResponseObject物件
if type(action_result) is dict or action_result is None:
resp_obj = ResponseObject(action_result)
#如果controller物件方法返回結果為ResponseObject物件
elif isinstance(action_result, ResponseObject):
resp_obj = action_result
#否則認為返回的結果是response物件
else:
response = action_result
#如果controller物件方法沒有返回response物件,則繼續處理resp_obj物件
if resp_obj:
#獲取controller物件方法 指定的序列化物件
serializers = getattr(meth, 'wsgi_serializers', {})
#繫結序列化物件
resp_obj._bind_method_serializers(serializers)
#獲取controller物件方法預設的HTTP code
if hasattr(meth, 'wsgi_code'):
resp_obj._default_code = meth.wsgi_code
#獲取accept報文格式下的序列化方法
#如果controller物件方法未指定序列化方法,則使用預設的序列化方法
resp_obj.preserialize(accept, self.default_serializers)
#執行HTTP請求的後向擴充套件方法
response = self.post_process_extensions(post, resp_obj,
request, action_args)
#如果後向方法沒有返回response物件
if resp_obj and not response:
#將controller物件方法返回結果 序列化
response = resp_obj.serialize(request, accept,
self.default_serializers)
if hasattr(response, 'headers'):
for hdr, val in response.headers.items():
# Headers must be utf-8 strings
response.headers[hdr] = utils.utf8(str(val))
if not request.api_version_request.is_null():
response.headers[API_VERSION_REQUEST_HEADER] = \
request.api_version_request.get_string()
response.headers['Vary'] = API_VERSION_REQUEST_HEADER
return response
(1)、獲取HTTP請求的引數,呼叫deserialize方法將 HTTP請求的訊息體反序列化成 字典物件。並且通過檢查專案ID
來驗證客戶是否有執行HTTP請求的許可權。
(2)、呼叫pre_process_extensions方法執行HTTP請求的前向擴充套件方法。前向擴充套件方法是為了便於二次開發預留的接
口。在Nova API處理HTTP請求的時候,會首先執行前向擴充套件方法,然後再執行底層Controller物件中的處理方法。
pre_process_extensions方法返回一個post物件,它是HTTP請求後向擴充套件方法的列表。後向擴充套件方法也是預留的二次
開發介面,會在底層的Controller物件的處理方法執行之後執行。
(3)、通過呼叫dispatch方法執行底層Controller物件的處理方法。底層Controller物件處理方法返回一個字典。
resp_obj = ResponseObject(action_result)將返回的結果封裝成ResponseObject物件。
(4)、對ResponseObject物件進行一些配置,需要配置的屬性有:序列化物件、預設的HTTP CODE。
(5)、ResponseObject物件的序列化物件和HTTP Code是在底層Controller物件的處理方法中指定。底層Controller對
象的每個處理方法都可以通過裝飾器指定序列化物件、反序列化物件和HTTP Code。例如:
指定了index方法的XML序列化物件為MinimalServersTemplate物件,如下程式碼片段:
指定了create方法的XML序列化物件為 FullServersTemplate物件,xml反序列化物件為CreateDeserializer,預設的
HTTP Code為202。當HTTP請求處理成功時,Nova API伺服器會向客戶端返回202 的HTTP Code。
(6)、HTTP請求的後向擴充套件方法,將ResponseObject物件序列化。
總結:由於前向擴充套件方法和後向擴充套件方法在Nova中沒有使用過,因此HTTP請求的核心工作依然在底層Controller類的
處理方法中定義。_process_stack方法主要是完成了資料的序列化和反序列化工作。