网站首页 > 博客文章 正文
概述:
Routes是python重新实现的Rails routes system,用来将urls映射到应用具体的action上。routes模块不仅仅是在openstack中进行使用,flask框架也是对其进行了封装。
WSGI:
Web服务器网关接口(python Web Server Gateway Interface)是为python语言定义的web服务器和web应用程序或框架之间的一种简单而通用的接口。
Wsgi区分为两个部分:服务器和应用程序。在处理一个WSGI请求时,服务器会为应用程序提供环境信息及一个回调函数,当应用程序完成处理后,透过前述的回调函数,将结果回传给服务器。
WSGI对于app对象有以下要求:
- 必须是一个可调用的对象
- 接收两个参数必选参数environ,start_response
- 返回值必须是可迭代的,用户表示http body。
class APPClass(object):
def __init__(**kwargs):
super(APPCLass, cls).__init__(**kwargs)
def function(environ, start_response):
pass
def __call__(environ, start_response):
pass
environ包含请求的所以信息,下面列举必须包含的变量:
- REQUEST_METHOD HTTP请求方法,例如GET,POST
- SCRIPT_NAME URL路径的起始部分对应的应用对象,如果应用程序对象对应服务器的根,那么这个值可以为空字符串。
- PATH_INFO URL除淋淋起始部分后的剩余部分,用于找到对应的应用程序对象。如果请求的路径是根路径,这个值为空字符串。
- QUERY_STRING URL路径中?后面的部分,GET的传参
- CONTENT_LENGTH HTTP请求中的Content-Length部分
- SERVER_NAME,SERVER_POR
- SERVER_PROTOCOL 客户端使用的协议,例如HTTP/1.0,HTTP/1.1,它决定了如何处理HTTP请求的头部。
start_response是HTTP相应的开始,被调用时服务器检查headers中的错误,禁止start_response直接将response_headers传递给客户端,它必须把它们存储起来,一直到应用程序第一次迭代返回一个非空数据后,才能将response_headers传递给客户端。
start_response(status, response_headers, exec_info=None)
# wsgiref工具参考文档:https://docs.python.org/zh-tw/3.11/library/wsgiref.html
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
# A relatively simple WSGI application. It's going to print out the
# environment dictionary after being updated by setup_testing_defaults
def simple_app(environ, start_response):
setup_testing_defaults(environ)
status = '200 OK'
headers = [('Content-type', 'text/plain; charset=utf-8')]
start_response(status, headers)
ret = [("%s: %s\n" % (key, value)).encode("utf-8")
for key, value in environ.items()]
return ret
with make_server('', 8000, simple_app) as httpd:
print("Serving on port 8000...")
httpd.serve_forever()
WSGI,uWSGI,uwsgi三者的关系:
WSGI是一个Python Web应用程序与Web服务器之间的接口规范,它定义了应用程序和服务器之间的标准接口,使得应用程序可以再不同的Web服务器上运行。
uWSGI是一个Web服务器,用C语言编写的Web应用程序容器。uWSGI服务器可以作为一个独立的应用服务器,也可以与其他Web服务器(比如Nginx,Apache)一起使用,通过WSGI协议与Python应用程序通信。
uwsgi是一个与uWSGI服务器相关的协议,uwsgi协议是一种二进制协议,它定义了uWSGI服务器与应用程序之间的通信协议。uwsgi协议允许uWSGI服务器与应用程序支架进行双向通信,从而提供了性能。
webob
Webob是一个封装了WSGI的请求和应答的python库。
Webob提供了多个对象,大部分都用来处理HTTP的请求,包括对HTTP头的解析,内容的处理,创建WSGI的应答(包括HTTP的状态,头和body)等。Webob最重要的两个类是Request和Response,Request主要用来构造和解析HTTP请求,而Response主要用来构造HTTP的应答。
def __call__(self, req, *args, **kw):
"""Call this as a WSGI application or with a request"""
func = self.func
if func is None:
if args or kw:
raise TypeError(
"Unbound %s can only be called with the function it "
"will wrap" % self.__class__.__name__)
func = req
return self.clone(func)
if isinstance(req, dict):
if len(args) != 1 or kw:
raise TypeError(
"Calling %r as a WSGI app with the wrong signature" %
self.func)
environ = req
start_response = args[0]
req = self.RequestClass(environ) #定义RequestClass
req.response = req.ResponseClass() # 定义ResponseClass
try:
args, kw = self._prepare_args(None, None)
resp = self.call_func(req, *args, **kw) # 回调函数
except HTTPException as exc:
resp = exc
if resp is None:
## FIXME: I'm not sure what this should be?
resp = req.response
if isinstance(resp, text_type):
resp = bytes_(resp, req.charset)
if isinstance(resp, bytes):
body = resp
resp = req.response
resp.write(body)
if resp is not req.response:
resp = req.response.merge_cookies(resp)
return resp(environ, start_response) # 返回数据
else:
args, kw = self._prepare_args(args, kw)
return self.call_func(req, *args, **kw)
方法介绍
routes.Mapper.connect()
routes.url()
routes.url_for()
routes.Mapper.resource()
routes.Mapper.redirect()
routes.Mapper.match()
使用方法
- 注册路由,路由名称'zbj', 路径是 '/clj', controller为 'main', action为 'index'# 匹配到此条路由URL的请求:交由controller类处理,请求预调用的函数index
map.connect('zbj', '/clj', controller='main', action='index')
- 匹配路由,match返回匹配的数据以及controller类,routematch比match多返回route对象。
map.connect('/home/{action:index|jia}/{id:\d+}', controller='home',action='index')
res = map.match('/home/jia/200')
print(res) # {'action': 'index', 'id': '200', 'controller': 'home'}
- 路由集合,具体参数使用方法自行百度
map.resource("message", 'message', controller=controller, path_prefix='/{project_id}',
name_prefix='lala_', collection={"list_many":'GET', 'create_many':'POST'},
member={'update_many':"POST", 'delete_many':"POST"},
new={'preview':"POST"},
parent_resource=dict(member_name='haha', collection_name='heihei'))
范例
参考openstack nova的源码进行梳理
def _create_controller(main_controller, action_controller_list): # 将实例函数注册到Resourced对象中
controller = wsgi.Resource(main_controller())
for ctl in action_controller_list: # 如果实例中存在被wsgi_action装饰的函数,将该函数属性保存。已备调用
controller.register_actions(ctl())
return controller
ROUTE_LIST = (
('/flavors', {
'GET': [flavor_controller, 'index'],
'POST': [flavor_controller, 'create']
})
)
# 使用偏函数完成类的注册
agents_controller = functools.partial(
_create_controller, agents.AgentController, [])
# paste加载app的入口
class APIRouterV21(base_wsgi.Router): # 继承父类
def __init__(self, custom_routes=None):
# 初始化实例,将map对象传给父类
super(APIRouterV21, self).__init__(nova.api.openstack.ProjectMapper())
for path, methods in ROUTE_LIST + custom_routes:
for method, controller_info in methods.items():
controller = controller_info[0]() # 控制类
action = controller_info[1] #动作
self.map.create_route(path, method, controller, action) # 将实例映射到routes.Mapper.connect(),供url映射调用
@classmethod
def factory(cls, global_config, **local_config):
return cls() # 返回实例对象
class Resource(wsgi.Application):
def __init__(self, controller):
self.controller = controller
self.default_serializers = dict(json=JSONDictSerializer)
self.wsgi_actions = {}
if controller:
self.register_actions(controller)
def register_actions(self, controller): # 被wsgi_action装饰器装饰的实例函数
actions = getattr(controller, 'wsgi_actions', {}) # 如何实现该方法,可以参考元类定义
for key, method_name in actions.items():
self.wsgi_actions[key] = getattr(controller, method_name)
def get_action_args(self, request_environment): # 解析匹配的参数,包含action,GET参数
if hasattr(self.controller, 'get_action_args'):
return self.controller.get_action_args(request_environment)
try:
args = request_environment['wsgiorg.routing_args'][1].copy()
except (KeyError, IndexError, AttributeError):
return {}
return args
def get_body(self, request):
content_type = request.get_content_type()
return content_type, request.body
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, request): # 调用实例,调用真正的应用程序
context = request.environ['nova.context']
action_args = self.get_action_args(request.environ) # 获取get参数
action = action_args.pop('action', None)
try:
content_type, body = self.get_body(request) # 获取body里面的数据
accept = request.best_match_content_type()
except exception.InvalidContentType:
msg = _("Unsupported Content-Type")
return Fault(webob.exc.HTTPUnsupportedMediaType(explanation=msg))
return self._process_stack(request, action, action_args, content_type, body, accept)
def _process_stack(self, request, action, action_args, content_type, body, accept):
meth = self.get_method(request, action, content_type, body)
contents = self._get_request_content(body, request)
action_args.update(contents)
response = None
try:
with ResourceExceptionHandler():
action_result = self.dispatch(meth, request, action_args) # 回调实例方法,返回返回值
except Fault as ex:
response = ex
if not response:
resp_obj = None
if type(action_result) is dict or action_result is None:
resp_obj = ResponseObject(action_result)
elif isinstance(action_result, ResponseObject):
resp_obj = action_result
else:
response = action_result
if resp_obj:
if hasattr(meth, 'wsgi_code'):
resp_obj._default_code = meth.wsgi_code
if resp_obj and not response:
response = resp_obj.serialize(request, accept)
return response
def get_method(self, request, action, content_type, body):
meth = self._get_method(request, action, content_type, body)
return meth
def _get_method(self, request, action, content_type, body):
try:
meth = getattr(self, action) if not self.controller else getattr(self.controller, action)
return meth
except AttributeError:
raise
action_name = action_peek(body) if action == 'action' else action
return (self.wsgi_actions[action_name])
def dispatch(self, method, request, action_args):
return method(req=request, **action_args)
class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None):
if url == "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)
def connect(self, *args, **kargs):
if not kargs['requirements'].get('format'):
kargs['requirements']['format'] = 'json|xml'
return routes.Mapper.connect(self, *args, **kargs)
class ProjectMapper(APIMapper):
def create_route(self, path, method, controller, action):
project_id_token = self._get_project_id_token()
self.connect('/%s%s' % (project_id_token, path), conditions=dict(method=[method]),
controller=controller, action=action)
self.connect(path, conditions=dict(method=[method]), controller=controller,
action=action)
class Request(webob.Request):
def __init__(self, environ, *args, **kwargs):
if CONF.wsgi.secure_proxy_ssl_header:
scheme = environ.get(CONF.wsgi.secure_proxy_ssl_header)
if scheme:
environ['wsgi.url_scheme'] = scheme
super(Request, self).__init__(environ, *args, **kwargs)
class Router(object):
def __init__(self, mapper):
self.map = mapper
self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map)
# 调取app,将url与实例的映射关系,回调函数传给RoutesMiddleware。该函数会进行url的匹配
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
return self._router #调取RoutesMiddleware
@staticmethod
@webob.dec.wsgify(RequestClass=Request)
def _dispatch(req): # 将url匹配成功app进行调用,调取Resouce类的__call__函数
match = req.environ['wsgiorg.routing_args'][1]
app = match['controller'] # Resource对象,封装真正的类对象
return app
# routes.middleware.py
class RoutesMiddleware(object):
def __init__(self, wsgi_app, mapper, use_method_override=True, path_info=True, singleton=True):
self.app = wsgi_app
self.mapper = mapper
self.path_info = path_info
def __call__(self, environ, start_response):
if self.singleton: 是否是单例模式
config = request_config()
config.mapper = self.mapper
config.environ = environ
match = config.mapper_dict
route = config.route
else:
results = self.mapper.routematch(environ=environ)
if results:
match, route = results[0], results[1] # 返回匹配参数(包括action,传入的参数),以及route对象
else:
match = route = None
url = URLGenerator(self.mapper, environ)
environ['wsgiorg.routing_args'] = ((url), match)
environ['routes.route'] = route
environ['routes.url'] = url
response = self.app(environ, start_response) # 回调函数
return response
class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None): # 在RoutesMiddler中进行调用
if url == "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)
def connect(self, *args, **kargs):
if not kargs['requirements'].get('format'):
kargs['requirements']['format'] = 'json|xml'
return routes.Mapper.connect(self, *args, **kargs)
参考文档
routes安装:
https://pypi.org/project/Routes/
routes文档:
https://routes.readthedocs.io/en/latest/
小计:
- 元类的使用方法
- openstack定时任务
- python3 异步函数
猜你喜欢
- 2025-04-05 每一个软件或者网站都会有这个让人恶心但又不可或缺的功能
- 2025-04-05 彻底理解Java反射以及动态代理中对反射的应用
- 2025-04-05 自定义代码生成器(上)(自定义代码块的作用)
- 2025-04-05 使用 Spring AI 和 DeepSeek 快速构建本地 AI 对话系统
- 2025-04-05 从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道
- 2025-04-05 SpringBoot系列-项目常用分层结构
- 2025-04-05 MyBatis的SQL执行流程不清楚?看完这一篇就够了
- 2025-04-05 如何深度理解mybatis?(如何深度理解两个必然与两个绝不会的科学性和合理性)
- 2025-04-05 高并发下实现幂等的几种方式(并发和幂等)
- 2025-04-05 十年之重修MyBatis原理(mybatis入门案例)
你 发表评论:
欢迎- 374℃手把手教程「JavaWeb」优雅的SpringMvc+Mybatis整合之路
- 369℃用AI Agent治理微服务的复杂性问题|QCon
- 360℃初次使用IntelliJ IDEA新建Maven项目
- 353℃Maven技术方案最全手册(mavena)
- 351℃安利Touch Bar 专属应用,让闲置的Touch Bar活跃起来!
- 348℃InfoQ 2024 年趋势报告:架构篇(infoq+2024+年趋势报告:架构篇分析)
- 346℃IntelliJ IDEA 2018版本和2022版本创建 Maven 项目对比
- 344℃从头搭建 IntelliJ IDEA 环境(intellij idea建包)
- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)