• Odoo 学习 【一】http & rpc


    HTTP

    Odoo 中http类中的Root是wsgi应用的入口主程序。

    入口,wsgi_server调用如下:

      def application(environ, start_response):
        if config['proxy_mode'] and '_X_FORWARDED_HOST' in environ:
            return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)
        else:
            return application_unproxied(environ, start_response)
    
      def application_unproxied(environ, start_response):
          ......
            wsgi_handlers = [wsgi_xmlrpc]
            wsgi_handlers += module_handlers   # module_handlers 处理器注册表,在http.py注册了root处理器。
            for handler in wsgi_handlers:
                result = handler(environ, start_response)
                if result is None:
                    continue
                return result
    

    注册root处理器,是一个单例对象,模块导入,就是单例的,handler是一个可调用对象,module_handlers维护了这样的一个列表。

      # register main wsgi handler
      root = Root()  # 这是一个可调用对象。
      openerp.service.wsgi_server.register_wsgi_handler(root) 
    
        def __call__(self, environ, start_response):
          """ Handle a WSGI request
          """
          if not self._loaded:
              self._loaded = True
              self.load_addons()
          return self.dispatch(environ, start_response)
    

    源码中,对dispath方法进行了进一步的包裹,Werkzeug 是一个 WSGI 工具包,environ包含了所有的信息,werkzeug.wrappers.Request对这个环境进行了进一步封装。在Odoo中则对这个原生的werkzeug.wrappers.Request对象进行了进一步的封装,可查看self.get_request方法。

    dispatch

      def dispatch(self, environ, start_response):
        """
        Performs the actual WSGI dispatching for the application.
        """
        try:
            httprequest = werkzeug.wrappers.Request(environ)
            httprequest.app = self
    
            explicit_session = self.setup_session(httprequest)
            self.setup_db(httprequest)
            self.setup_lang(httprequest)
    
            request = self.get_request(httprequest)
    
            def _dispatch_nodb():
                try:
                    func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
                except werkzeug.exceptions.HTTPException, e:
                    return request._handle_exception(e)
                request.set_handler(func, arguments, "none")
                result = request.dispatch()
                return result
    
            with request:
                db = request.session.db
                if db:
                    openerp.modules.registry.RegistryManager.check_registry_signaling(db)
                    try:
                        with openerp.tools.mute_logger('openerp.sql_db'):
                            ir_http = request.registry['ir.http']
                    except (AttributeError, psycopg2.OperationalError):
                        # psycopg2 error or attribute error while constructing
                        # the registry. That means the database probably does
                        # not exists anymore or the code doesnt match the db.
                        # Log the user out and fall back to nodb
                        request.session.logout()
                        result = _dispatch_nodb()
                    else:
                        result = ir_http._dispatch()
                        openerp.modules.registry.RegistryManager.signal_caches_change(db)
                else:
                    result = _dispatch_nodb()
    
                response = self.get_response(httprequest, result, explicit_session)
            return response(environ, start_response)
    

    上述的封装过的request是一个上下文管理器,也就是定义了__enter__,__exit__方法。在WebRequest父类中,可窥斑见豹了,具体如下:

        def __enter__(self):
            _request_stack.push(self)  # 压入请求栈中
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            _request_stack.pop() # 弹出请求栈
    
            if self._cr:
                if exc_type is None and not self._failed:
                    self._cr.commit()
                self._cr.close()
            # just to be sure no one tries to re-use the request
            self.disable_db = True
            self.uid = None
    

    _request_stack是一个werkzeug.local.LocalStack()对象,LocalStack使用Local(类似于threading.local)实现的栈结构,可以将对象推入、弹出,也可以快速拿到栈顶,所有的修改在本线程(在绿色线程内优先使用Greenlet的ID)内可见。_request_stack是一个可调用对象的实例,_request_stack(),可快速拿到栈顶元素,request=_request_stack,这样访问导入request对象,也就永远是栈顶元素,即当前请求对象。

    下面是真正的调度,不过什么也看不出,Command+左键进去。

    result = ir_http._dispatch()
    
      def _find_handler(self, return_rule=False):
        return self.routing_map().bind_to_environ(request.httprequest.environ).match(return_rule=return_rule)
    

    ir_http这个实例保存着所有已经安装模块的路由映射,route_map。_find_handler会根据request(Odoo栈顶请求请求).httprequest(werkzerg请求对象).environ信息,会在这个路由映射中查找出对应的处理规则。

        def _dispatch(self):
            # locate the controller method
            try:
                rule, arguments = self._find_handler(return_rule=True)
                func = rule.endpoint
            except werkzeug.exceptions.NotFound, e:
                return self._handle_exception(e)
    
            # check authentication level
            try:
                auth_method = self._authenticate(func.routing["auth"])
            except Exception as e:
                return self._handle_exception(e)
    
            processing = self._postprocess_args(arguments, rule)
            if processing:
                return processing
    
    
            # set and execute handler
            try:
                request.set_handler(func, arguments, auth_method)
                result = request.dispatch()
                if isinstance(result, Exception):
                    raise result
            except Exception, e:
                return self._handle_exception(e)
    
            return result
    

    get_request

    推测,并进行进一步封装,根据分类,包装成JsonRequest和HttpRequest对象,它们都是的Odoo WebRequest的子类,包含了uid,环境,用户上下文等这些信息。

      def get_request(self, httprequest):
        # deduce type of request
        if httprequest.args.get('jsonp'):
            return JsonRequest(httprequest)
        if httprequest.mimetype in ("application/json", "application/json-rpc"):
            return JsonRequest(httprequest)
        else:
            return HttpRequest(httprequest)
    

    RPC

    Odoo WSGI Server应该也注意到还有一个wsgi_xmlrpc,也就是为了兼容xml-rpc,xml与json之间的转换,在源码中看出是最原始的wsgi的应用。
    wsgi_xmlrpc

    def wsgi_xmlrpc(environ, start_response):
        if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
            length = int(environ['CONTENT_LENGTH'])
            data = environ['wsgi.input'].read(length)
            string_faultcode = True
            if environ['PATH_INFO'].startswith('/xmlrpc/2/'):
                service = environ['PATH_INFO'][len('/xmlrpc/2/'):]
                string_faultcode = False
            else:
                service = environ['PATH_INFO'][len('/xmlrpc/'):]
            params, method = xmlrpclib.loads(data)
            return xmlrpc_return(start_response, service, method, params, string_faultcode)
    

    xmlrpc_return

      def xmlrpc_return(start_response, service, method, params, string_faultcode=False):
        try:
            result = openerp.http.dispatch_rpc(service, method, params)
            response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
        except Exception, e:
            if string_faultcode:
                response = xmlrpc_handle_exception_string(e)
            else:
                response = xmlrpc_handle_exception_int(e)
        start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
        return [response]
    

    Common RPC

    在http.py模块下定义CommonController,对应的路由是/jsonrpc。
    具体是通过dispath_rpc调用。
    Odoo Service 目录下存在下面的四个文件,分别对应四种服务,都拥有dispatch()方法来,来调用各自模块下的方法。

    • common.py
      login、authenticate、version、about、set_loglevel
    • db.py
      create_database、duplicate_database、drop、dump、restore、rename、change_admin_password、migrate_database、db_exist、list、list_lang、list_countries、server_version
    • model.py
      execute、execute_kw、execute_workflow
    • report.py
      report、report_get、render_report

    DataSet RPC

    在web DataSet 控制器定义的路由。

    • /web/dataset/search_read
    • /web/dataset/load
    • /web/dataset/call
    • /web/dataset/call_kw
    • /web/dataset/call_buttion
    • /web/dataset/exec_workflow
    • /web/dataset/resequence
  • 相关阅读:
    webmagic使用
    网站文件下载链接
    正则表达式
    JS 页面刷新或重载
    History
    【问题&解决】fonts/fontawesome-webfont.woff2 404 (Not Found)
    ckeditor的使用
    Windows Server 2012 R2 或 2016 无法安装 .Net 3.5.1
    自定义配置文件的读取
    MVC中上传文件大小限制的解决办法
  • 原文地址:https://www.cnblogs.com/baishoujing/p/8998801.html
Copyright © 2020-2023  润新知