(学习笔记向)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

 

 


 

 关于路径和视图函数的绑定过程分析

源码的地址https://github.com/xxw11/light-xflask

posted on 2020-05-24 20:22  xwwer  阅读(331)  评论(0)    收藏  举报