Nova API服务 之 处理HTTP请求的流程

来源:互联网 发布:淘宝联盟手安卓版 编辑:程序博客网 时间:2024/06/03 19:14

一、回顾

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方法主要是完成了数据的序列化和反序列化工作


0 0
原创粉丝点击