(学习笔记向)python轻量级web框架原理(一)wsgi,路由与视图函数的绑定

基本介绍
-
werkzeug作为HTTP 和 WSGI 相关的工具集
-
jinja2模板引擎
- pymysql作为orm的底层
wsgi和运行的基本流程
wsgi是作为Server 和 应用(我们的代码) 通信的“桥梁”,
服务器通过调用我们的应用,并传给它两个参数:
environ 包含了请求的所有信息,
start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。

一,web框架和sever交互的环节
模仿Flask框架Flask对象
我们创建一个app.py,写入XFlask对象
1 class XFlask(object): 2 3 def wsgi_app(self,environ,start_response): 4 '''处理request并创建返回对象的过程略去''' 5 return response(environ, start_response) 6 7 def __call__(self, environ, start_response): 8 return self.wsgi_app(environ, start_response) 9 10 11 def run(self, port=8000, ip='127.0.0.1', debug=True): 12 run_simple(ip, port, self, use_debugger=debug, use_reloader=True) 13
当我们创建app实例时(app=XFlask())
XFlask的方法 __call__ 就会被调用
紧接着调用wsgi_app这个函数处理request请求
并最终返回response对象
app.run()调用的是werkzeug.serving中run_simple
这就是web框架和sever交互的环节
二.对不同请求路径方法的处理
在一中说到服务器传的environ 包含了请求的所有信息
我们可以通过这些信息来进行 不同路径 不同方法 编写视图(view)函数进行处理
比如我们编写一个view.py要对不同路径 不同方法 用(view)函数进行处理
import json from app import View, render_template, session, XFlask class Index(View): def GET(self,request): session['hello'] = 2 return render_template("index.html") def POST(self,request): return json.dumps({'1':'hello'}) class Test(View): def GET(self,request): print(session['hello']) return render_template("test.html") def POST(self, request): return json.dumps({'2': 'hello'}) urls={'/':Index, '/test':Test} if __name__ == '__main__': app=XFlask() app.add_url_rule(urls) app.run()
werkzeug可以帮我们比较轻松完成这个工作,
我们在XFlask中继续添加__init__的方法,在创建实例的过程中会被执行,
初始化self.url_map和self.view
from werkzeug.routing import Map, Rule class XFlask(object): def __init__(self): self.url_map = Map() self.view = {}
我们还需要将view.py中的视图函数和对应url添加到进来,编写处理视图函数的类
1 class XFlask(object): 2 def dispatch_request(self, req): 3 adapter = self.url_map.bind_to_environ(req.environ) 4 self.url_map.bind_to_environ(req.environ) 5 try: 6 endpoint, value = adapter.match() 7 return self.view[endpoint](req, **value) 8 except HTTPException as e: 9 response = e 10 return response 11 12 def add_url_rule(self,urls): 13 #for url in urls: 14 #self.url_map[url] = urls[url].get_func() 15 for url in urls: 16 self.url_map.add(Rule(url,endpoint=str(urls[url]))) 17 self.view[str(urls[url])] = urls[url].get_func() 18 19 class View(object): 20 """Baseclass for our views.""" 21 def __init__(self): 22 self.methods_meta = { 23 'GET': self.GET, 24 'POST': self.POST, 25 'PUT': self.PUT, 26 'DELETE': self.DELETE, 27 } 28 def GET(self): 29 raise MethodNotAllowed() 30 POST = DELETE = PUT = GET 31 32 def HEAD(self): 33 return self.GET() 34 35 def dispatch_request(self, request, *args, **options): 36 if request.method in self.methods_meta: 37 return self.methods_meta[request.method](request, *args, **options) 38 else: 39 return '<h1>Unsupported require method</h1>' 40 41 @classmethod 42 def get_func(cls): 43 def func(*args, **kwargs): 44 obj = func.view_class() 45 return obj.dispatch_request(*args, **kwargs) 46 func.view_class = cls 47 return func
关于类方法(@classmethod)不了解的同学可以百度一下
同时我们补充XFlask wsgi_app中的处理,为了方便调用我们继承(无变动)了
werkzeug.wrappers中的Request, Response的两个对象,
同时我们模仿flask编写请求上下文(request context),它包含所有请求相关的信息。
它会在请求进入时被创建, 然后被推送到_request_ctx_stack
(其中_request_stk = LocalStack() 而LocalStack是werkzeug.local中的工具,用来创建本地的栈(一种数据结构)),
from werkzeug.wrappers import Request, Response class Request(Request): """Encapsulates a request.""" class Response(Response): """Encapsulates a response.""" class _RequestContext(object): """请求上下文(request context)包含所有请求相关的信息。它会在请求进入时被创建, 然后被推送到_request_ctx_stack,在请求结束时会被相应的移除。它会为提供的 WSGI环境创建URL适配器(adapter)和请求对象。 """ def __init__(self, app, environ): self.app = app self.url_adapter = app.url_map.bind_to_environ(environ) # url适配器 self.request = Request(environ) self.session = app.open_session(self.request) def __enter__(self): # print(self.test) _request_stk.push(self) def __exit__(self, exc_type, exc_value, tb): # 在调试模式(debug mode)而且有异常发生时,不要移除(pop)请求堆栈。 # 这将允许调试器(debugger)在交互式shell中仍然可以获取请求对象。 if tb is None or not self.app.debug: _request_stk.pop() class XFlask(object): def wsgi_app(self,environ,start_response): with self.request_context(environ): req = Request(environ) response = self.dispatch_request(req) # print(type(response)) if response:#如果可以找到正确的匹配项 response = self.make_response(response) response = self.process_response(response) response.content_type="text/html" response.charset="UTF-8" # response = Response(response, content_type='text/html; charset=UTF-8') else:#找不到,返回404NotFound response = Response('<h1>404 Not Found<h1>', content_type='text/html; charset=UTF-8', status=404) return response(environ, start_response)
关于其中的with语句 https://www.cnblogs.com/lyfstorm/p/11063189.html

关于路径和视图函数的绑定过程分析
浙公网安备 33010602011771号