动手写一个简单的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框架”

猜你喜欢

转载自www.cnblogs.com/JoshuaYu/p/12951160.html