HTTP

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

入口,wsgi_server调用如下:

  1. def application(environ, start_response):
  2. if config['proxy_mode'] and '_X_FORWARDED_HOST' in environ:
  3. return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)
  4. else:
  5. return application_unproxied(environ, start_response)
  6. def application_unproxied(environ, start_response):
  7. ......
  8. wsgi_handlers = [wsgi_xmlrpc]
  9. wsgi_handlers += module_handlers # module_handlers 处理器注册表,在http.py注册了root处理器。
  10. for handler in wsgi_handlers:
  11. result = handler(environ, start_response)
  12. if result is None:
  13. continue
  14. return result

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

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

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

dispatch

  1. def dispatch(self, environ, start_response):
  2. """
  3. Performs the actual WSGI dispatching for the application.
  4. """
  5. try:
  6. httprequest = werkzeug.wrappers.Request(environ)
  7. httprequest.app = self
  8. explicit_session = self.setup_session(httprequest)
  9. self.setup_db(httprequest)
  10. self.setup_lang(httprequest)
  11. request = self.get_request(httprequest)
  12. def _dispatch_nodb():
  13. try:
  14. func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
  15. except werkzeug.exceptions.HTTPException, e:
  16. return request._handle_exception(e)
  17. request.set_handler(func, arguments, "none")
  18. result = request.dispatch()
  19. return result
  20. with request:
  21. db = request.session.db
  22. if db:
  23. openerp.modules.registry.RegistryManager.check_registry_signaling(db)
  24. try:
  25. with openerp.tools.mute_logger('openerp.sql_db'):
  26. ir_http = request.registry['ir.http']
  27. except (AttributeError, psycopg2.OperationalError):
  28. # psycopg2 error or attribute error while constructing
  29. # the registry. That means the database probably does
  30. # not exists anymore or the code doesnt match the db.
  31. # Log the user out and fall back to nodb
  32. request.session.logout()
  33. result = _dispatch_nodb()
  34. else:
  35. result = ir_http._dispatch()
  36. openerp.modules.registry.RegistryManager.signal_caches_change(db)
  37. else:
  38. result = _dispatch_nodb()
  39. response = self.get_response(httprequest, result, explicit_session)
  40. return response(environ, start_response)

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

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

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

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

  1. result = ir_http._dispatch()
  1. def _find_handler(self, return_rule=False):
  2. 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信息,会在这个路由映射中查找出对应的处理规则。

  1. def _dispatch(self):
  2. # locate the controller method
  3. try:
  4. rule, arguments = self._find_handler(return_rule=True)
  5. func = rule.endpoint
  6. except werkzeug.exceptions.NotFound, e:
  7. return self._handle_exception(e)
  8. # check authentication level
  9. try:
  10. auth_method = self._authenticate(func.routing["auth"])
  11. except Exception as e:
  12. return self._handle_exception(e)
  13. processing = self._postprocess_args(arguments, rule)
  14. if processing:
  15. return processing
  16. # set and execute handler
  17. try:
  18. request.set_handler(func, arguments, auth_method)
  19. result = request.dispatch()
  20. if isinstance(result, Exception):
  21. raise result
  22. except Exception, e:
  23. return self._handle_exception(e)
  24. return result

get_request

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

  1. def get_request(self, httprequest):
  2. # deduce type of request
  3. if httprequest.args.get('jsonp'):
  4. return JsonRequest(httprequest)
  5. if httprequest.mimetype in ("application/json", "application/json-rpc"):
  6. return JsonRequest(httprequest)
  7. else:
  8. return HttpRequest(httprequest)

RPC

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

wsgi_xmlrpc

  1. def wsgi_xmlrpc(environ, start_response):
  2. if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
  3. length = int(environ['CONTENT_LENGTH'])
  4. data = environ['wsgi.input'].read(length)
  5. string_faultcode = True
  6. if environ['PATH_INFO'].startswith('/xmlrpc/2/'):
  7. service = environ['PATH_INFO'][len('/xmlrpc/2/'):]
  8. string_faultcode = False
  9. else:
  10. service = environ['PATH_INFO'][len('/xmlrpc/'):]
  11. params, method = xmlrpclib.loads(data)
  12. return xmlrpc_return(start_response, service, method, params, string_faultcode)

xmlrpc_return

  1. def xmlrpc_return(start_response, service, method, params, string_faultcode=False):
  2. try:
  3. result = openerp.http.dispatch_rpc(service, method, params)
  4. response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
  5. except Exception, e:
  6. if string_faultcode:
  7. response = xmlrpc_handle_exception_string(e)
  8. else:
  9. response = xmlrpc_handle_exception_int(e)
  10. start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
  11. 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

Odoo 学习【一】http & rpc的更多相关文章

  1. scala学习之实现RPC通信

    最近学习scala,个人感觉非常灵活,实现rpc通信非常简单,函数式编程比较烧脑 1.搭建工程 创建scala maven 工程 项目pom文件 <project xmlns="htt ...

  2. odoo学习

    odoo视图对应模型:model="ir.ui.view"> <record id="mrp_workcenter_view_light_inherit&qu ...

  3. odoo学习总结

                                                   odoo10总结 1.odoo中的向导应用. .py文件 # -*- coding: utf-8 -*-f ...

  4. 学习别人的rpc框架

    https://my.oschina.net/huangyong/blog/361751 https://gitee.com/huangyong/rpc 在此文基础上的另一个实现,解决了原文中一些问题 ...

  5. Odoo 学习地址

    Odoo官文文档: https://www.odoo.com/zh_cn/page/docs http://www.odoo.com/documentation/8.0/ Odoo中文文档推荐: ht ...

  6. 【转】Java学习---快速掌握RPC原理及实现

    [原文]https://www.toutiao.com/i6592365493435236872/ ​RPC概述 RPC(Remote Procedure Call)即远程过程调用,也就是说两台服务器 ...

  7. RESTful源码学习笔记之RPC和 RESTful 什么区别

    REST,即Representational State Transfer的缩写.翻译过来是表现层状态转换.如果一个架构符合REST原则,就称它为RESTful架构.啥叫json-rpc?接口调用通常 ...

  8. RESTful源码学习笔记之RPC和Restful深入理解

    以下资料搜集自网络 0x00 RPC RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法).通常的实现 ...

  9. odoo学习之:【转】控制menuitem的显示权限

    作者原文:https://blog.csdn.net/wangnan537/article/details/43992771 在实际应用Odoo(OpenERP)的过程中, 会有对某用户组隐藏菜单的需 ...

随机推荐

  1. 2、MyEclipse和Eclipse调优,MyEclipse配置(tomcat和jdk的内存设置),jar引入相关知识点,将Java项目编程web项目的办法

    1.WindowàPreferenceàGeneralàWorkspaceàText file encoding都改成UTF-8 2.WindowàPreferenceàGeneralàEdito ...

  2. Elcipse安装gradle插件

    参考:             http://www.gradle.org/docs/current/userguide/installation.html (1)下载Gradle 官网下载www.g ...

  3. CentOS下Mariadb表名大小写的问题

    今天在linux上跑一个系统 发现数据库报错,说找不到表 问题是,我已经建立了表呀. 我把报错的那个表 复制到命令行 运行一下. 发现是大小写的问题. 那问题就简单了. 网上百度可以知道 打开/etc ...

  4. android scrollview嵌套listview计算高度的问题

    ScrollView中只能放一个控件,一般都放LinearLayout,orientation属性值为vertical.在LinearLayout中放需要呈现的内容.ListView也在其中,List ...

  5. 【一天一道LeetCode】#101. Symmetric Tree

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  6. UNIX环境高级编程——进程环境

    一.main函数 C程序总是从main函数开始.当内核执行C程序时,在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址--这是由连接编译器设置的,而连接编译器则由 ...

  7. Leetcode_102_Binary Tree Level Order Traversal

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/41929059 Given a binary tree, r ...

  8. 17_Android中Broadcast详解(有序广播,无序广播)最终广播,Bundle传递参数,传递参数的时候指定权限

     1  Broadcast是Android中的四大组件之一,他的用途很大,比如系统的一些广播:电量低.开机.锁屏等一些操作都会发送一个广播. 2  广播被分为两种不同的类型:"普通广播( ...

  9. 让你的动画不再生硬 Android插值器Interpolator使用秘籍

    有木有厌烦生硬的动画效果,想不想让你的动画变得圆滑且 欢迎收看本期的走进科学... 停,停,别打了,(.﹏.*) 我错了-- 我们要达到的效果: 先来回顾一下普通动画的用法吧. * 缩放动画 Scal ...

  10. android沉浸式状态栏的实现

    在style.xml中添加 [html] view plaincopy <style name="Theme.Timetodo" parent="@android: ...