一、回顾
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类的初始化方法定义如下:
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)
......
Resource类是在资源的底层Controller类的基础上,添加了
序列化和反序列化的功能。
序列化:是指将XML或JSON格式的数据转化成字符串格式,以便于在网络间传输
反序列化:将字符串数格式的数据转换为XML或JSON数据格式,以便于显示和处理。 所以,在Resource类中通过两个
成员变量default_deserializers和default_serializers来分别实现数据的反序列化和序列化。
2、__call__方法
Resource类的__call__方法定义如下:
@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)
可以看出,__call__方法最后调用了_process_stack方法。
_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方法主要是完成了数据的序列化和反序列化工作。