• 动手写一个简单的Web框架(Werkzeug路由问题)


    动手写一个简单的Web框架(Werkzeug路由问题)

    继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的,所以在这篇博客中,将实现一个简单的路由自定义功能

    首先引入werkzeug中的两个工具,分别是Map,和Rule,需要通过以下代码引入

    from werkzeug.routing import Map, Rule
    

    这两个可以完成路由定义和匹配的基本实现,其次,还引入到werkzeug中的Request对象来处理environ

    首先,我们看一下Map和Rule的使用:

    from werkzeug.routing import Rule, Map
    url_map = Map([
        Rule('/', endpoint='index', methods=['GET']),
        Rule('/about', endpoint='about', methods=['GET']),
    ])
    print(url_map)
    

    上面这段代码定义了一个url_map的对象,它是Map的实例,传入参数是一个列表,在列表里面,存储着路由,我们一个一个来看,Rule中首先传入的是路由参数,代表着访问的路由,然后是methods,用于标记请求的方法,再接着是endpoint,这个参数其实只是给这个路由起一个名字,但是关系确是重大的,我们下一步进行的视图函数的定义以及和路由绑定在一起就离不开endpoint这个参数,它可由使用者自行定义,也可默认为视图函数的函数名。可以看到打印出来的url_map,是路由和endpoint以及methods一一对应的

    Map中有一个add方法,可以用于添加新的路由

    from werkzeug.routing import Rule, Map
    url_map = Map([
        Rule('/', endpoint='index', methods=['GET']),
        Rule('/about', endpoint='about', methods=['GET']),
    ])
    url_map.add(Rule('/hello', endpoint='hello', methods=['GET']))
    print(url_map)
    

    可以看到运行结果

    关于路由的匹配问题,可以使用Map的match来进行匹配,用法只需传入路由,即可获取endpoint,在运行前,需要使用Map中的bind,来实例化一个urls,它必需的参数为hostname,还有其他的参数可自行研究,我们需要根据实例化出的urls中的match来实现路由的匹配:

    from werkzeug.routing import Rule, Map
    url_map = Map([
        Rule('/', endpoint='index', methods=['GET']),
        Rule('/about', endpoint='about', methods=['GET']),
    ])
    url_map.add(Rule('/hello', endpoint='hello', methods=['GET']))
    print(url_map)
    urls = url_map.bind('127.0.0.1:5000')
    url = '/'
    endpoint = urls.match(path_info=url)
    print(endpoint)
    

    运行结果

    看到,匹配到了路由的endpoint

    那么,endpoint要怎么使用呢?

    我们在添加视图函数的时候,只是添加了路由,我们对于视图函数的存储还没有实现,其实,这里需要自己定义一个字典,用于存储endpoint和视图函数的键值对,这样,就可以通过获取和存入的endpoint来实现获取和存入视图函数,这部分代码就整合到所有代码中:

    from werkzeug.wrappers import Response, Request
    from werkzeug.serving import run_simple
    from werkzeug.routing import Map, Rule
    
    class Jlask(object):
        # 初始化url_map和存储endpoint对应视图函数
        # 这里是一个小坑,不能再__init__中初始化,这样会使得在类被调用的时候,执行了初始化的代码,导致原先创建的路由都丢失
        url_map = Map([])
        endpoint_dict = {}
    
        def dispatch_request(self, request):
            # 获取请求的路由
            url = request.path
            urls = self.url_map.bind('127.0.0.1:5000')
            # 匹配得到endpoint
            endpoint = urls.match(path_info=url)[0]
            # 获取到视图函数处理得结果
            view_func = self.endpoint_dict[endpoint](request)
            return Response(view_func)
    
        def wsgi_app(self, environ, start_response):
            # 启动
            # 实例化request,存入environ,这里的request就是werkzeug中对于environ的处理,可以通过request来获取各种请求信息
            request = Request(environ)
            # 调用dispatch_request解析url,得到对应的视图函数处理的结果
            response = self.dispatch_request(request)
            return response(environ, start_response)
    
        def __call__(self, environ, start_response):
            # 调用到wsgi_app,来执行url对应视图函数
            return self. wsgi_app(environ, start_response)
    
    
        def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
            # 获取请求方法
            methods = options.pop('methods', None)
            # 如果没有定义endpoint,就用试图函数的名字
            if endpoint is None:
                endpoint = view_func.__name__
                # 生成rule
                # rule: 定义的路由
                self.url_map.add(Rule(rule, endpoint=endpoint, methods=methods))
                # 存储endpoint对应的视图函数
                self.endpoint_dict[endpoint] = view_func
                # print(self.endpoint_dict)
                # print(self.url_map)
    
    app = Jlask()
    
    
    def Hello(request):
        return "Hello World"
    app.add_url_rule(rule='/', view_func=Hello, methods=['GET'])
    
    
    
    if __name__ == '__main__':
        run_simple('127.0.0.1', 5000, Shortly())
    

    到此就完成了简单得包含路由匹配功能的“Web框架”


    更新:

    对于自定义的视图函数,需要考虑使用者是否自己构造Response对象,所以需要在dispatch_request中对调用的视图函数返回值进行判断,如果是字符串对象,就构造Response,否则就直接返回

    def dispatch_request(self, request):
        url = request.path
        urls = self.url_map.bind('127.0.0.1:5000')
        endpoint = urls.match(path_info=url)[0]
        view_func = self.endpoint_dict[endpoint](request)
        if isinstance(view_func, str):
            return Response(view_func)
        else:
            return view_func
    
  • 相关阅读:
    Kinect学习笔记(六)——深度数据测量技术及应用
    [device]/proc/devices and /dev/
    [Eth]Mac/Phy/mdio/Rgmii
    [uboot]uboot如何引导系统
    [网络]Linux一些网络知识
    [基础]sizeof和strlen
    [基础]关于extern指针和数组的用法
    [ucos]了解ucos
    [Linux]gcc/libc/glibc
    [i.MX6q]i.MX6q处理器,linux操作系统平台搭建 从SD卡启动系统
  • 原文地址:https://www.cnblogs.com/JoshuaYu/p/12951160.html
Copyright © 2020-2023  润新知