pythonFlask之路由

Posted sysu_lluozh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pythonFlask之路由相关的知识,希望对你有一定的参考价值。

一、关于路由

所谓路由,就是处理请求URL和函数之间关系的程序

根据例子看源码

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'hello world!'

if __name__ == '__main__':
    app.run()

Flask中对URL规则进行统一管理的,创建URL规则有两种方式:

  1. 使用@app.route修饰器
    传入URL规则作为参数,将函数绑定到URL,这个过程便将一个函数注册为路由,这个函数则被称为视图函数

  2. 使用app.add_url_rule()

二、注册路由

定位到@app.route修饰器,看看route是什么

def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop("endpoint", None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator

很明显,这是一个装饰器,route()有两个参数,rule表示url规则
该函数对参数进行处理之后,调用方法add_url_role(),这里也就验证了两种注册路由的方法等价

继续定位add_url_rule这个方法:

@setupmethod
    def add_url_rule(
        self,
        rule,
        endpoint=None,
        view_func=None,
        provide_automatic_options=None,
        **options
    ):
   ....

这个方法传入的参数有这些:

  • rule: url规则
  • endpoint: 要注册规则的endpoint,默认是视图函数的名
  • view_func: 视图函数
  • provide_automatic_options: 请求方法是否添加OPTIONS方法的一个标志
  • options: 关于请求处理的一些方法等

三、endpoint

着重介绍下endpoint,中文意思是端点
flask内部先把URL地址映射到端点(endpoint)上,然后再映射到视图函数(view_func)
看一个例子:

from flask import Flask

app = Flask(__name__)

# 这里我们加个参数endpoint
@app.route('/', endpoint="say_hello")
def index():
    return 'hello world!'


if __name__ == '__main__':
    app.run()

继续看源码add_url_rule:

...
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options["endpoint"] = endpoint
        methods = options.pop("methods", None)

这句话的意思是说如果endpoint没有, 就调用_endpoint_from_view_func方法,看一下_endpoint_from_view_func方法知道它其实就是返回view_func的名称

def _endpoint_from_view_func(view_func):
    """Internal helper that returns the default endpoint for a given
    function.  This always is the function name.
    """
    assert view_func is not None, "expected view func if endpoint is not provided."
    return view_func.__name__

所以通常来说,endpoint 不写,默认值就是和view_func同一个名称

作用

那么它的作用是什么呢?
端点通常用作反向查询URL地址(viewfunction–>endpoint–>URL)

例如,在flask中有个视图,想把它关联到另一个视图上(或从站点的一处连接到另一处), 可以直接使用URL_for()
看一个例子:

from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route('/')
def index():
    # url_for第一个参数就是endpoint
    login_url = url_for('login', name="tony")
    print(login_url)  # 打印 /login/tony
    return redirect(login_url)  # 自动跳转到另一个URL所在的地址
    return '这是首页'

@app.route('/login/<name>')
def login(name):
    print("name=", name)
    return '这是登录页面'

if __name__ == '__main__':
    app.run()  # 默认端口5000

四、url_for()与重定向

url(路由)和函数视图的映射关系,即通过对应的url可以返回对应的视图函数。当然通过视图函数也能找到对应的url路径,这可以通过url_for来完成

4.1 url_for

在浏览网页的时候都会有跳转,假设写跳转时用url路径,当整个网站路径需要修改时需要更改这些代码,如果使用的是url_for修改量就很少(通常视图函数是不修改)
看看url_for的使用:

@app.route('/post/list/<page>/')
def my_list(page):
    return 'my list'

print(url_for('my_list',page=1,count=2))
# 构建出来的url:/my_list/1/?count=2

4.2 重定向

重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此应重定向到登录页面
具体操作如下:

from flask import Flask,request,redirect,url_for

app = Flask(__name__)

@app.route('/login/')
def login():
    return '这是登录页面'

@app.route('/profile/')
def profile():
    if request.args.get('name'):
        return '个人中心页面'
    else:
        # redirect 重定向
        return redirect(url_for('login'))

五、路由匹配

在应用初始化的过程中,会注册所有的路由规则,可以调用app.url_map查看,当服务收到URL请求时,就需要进行路由匹配,以找到对应的视图函数,对应的流程和原理是什么呢?

当用户请求进入Flask应用时,首先会调用Flask里面的__call__方法,这个方法会调用Flask类的wsgi_app方法:

class Flask(_PackageBoundObject):
....
    def __call__(self, environ, start_response):
        """The WSGI server calls the Flask application object as the
        WSGI application. This calls :meth:`wsgi_app` which can be
        wrapped to applying middleware."""
        return self.wsgi_app(environ, start_response)

定位wsgi_app方法:

def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

该函数的处理过程包括:

  1. 创建RequestContext对象,在对象初始化的过程中调用app.create_url_adapter()方法,将请求参数environ传给Map对象创建MapAdapter对象,保存在url_adapter字段中
  2. 将RequestContext对象推入_request_ctx_stack栈中
  3. 通过RequestContext的match_request方法,调用MapAdapter对象的match方法找到匹配的Rule并解析出参数,保存在request的url_rule和view_args字段中
  4. 调用full_dispatch_request()

接下来看下full_dispatch_request方法:

def full_dispatch_request(self):
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            rv = self.preprocess_request()
            if rv is None:
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)

看到这里调用了self.dispatch_request()方法:

def dispatch_request(self):

        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        if (
            getattr(rule, "provide_automatic_options", False)
            and req.method == "OPTIONS"
        ):
            return self.make_default_options_response()
        # otherwise dispatch to the handler for that endpoint
        return self.view_functions[rule.endpoint](**req.view_args)

处理的过程是:

  1. 获取请求对象的request,找到对应的endpoint
  2. 从view_functions中找到对应的视图函数,传递请求参数
  3. 视图函数处理内部逻辑并返回,完成一次请求分发

六、动态路由

6.1 动态路由传参

http://127.0.0.1:5000/student_list/2/

在path中有可变的部分,达到了传参的目的,称之为动态路由传参

@app.route('/student_list/<student_id>/')
def student_list(student_id):
    return '学生号的信息'.format(student_id)

6.2 动态路由的过滤

可以对参数限定数据类型,比如限定为student_id必须为整数类型

http://127.0.0.1:5000/student_list/2/
@app.route('/student_list/<int:student_id>/')
def article_detail(student_id):
    print(student_id, type(student_id))
    return '学生号的信息'.format(student_id)

主要有这几种类型过滤:

  • string: 默认的数据类型,接收没有任何斜杠" /"的字符串
  • int: 整型
  • float: 浮点型
  • path: 和string类型相似,但是接受斜杠,如:可以接受参数/aa/bb/cc/多条放在一起
  • uuid: 只接受uuid格式的字符串字符串

提示:uuid为全宇宙唯一的串

七、视图函数的URL路由设置

7.1 app.url_map查看所有路由

Flask的路由可以通过视图函数的修饰器@app.route()来配置访问url
在Django中可以在urls.py中查看所有视图的url信息,那么Flask如何查看所有视图url的信息呢?

可以看到日志中打印出了url的信息

7.2 使用methods设置视图请求的方式

在Flask的视图配置了url之后,默认是只支持GET请求的。如果需要处理POST请求,则需要使用methods参数设置一下

# 通过methods设置GET\\POST请求
@app.route('/post_only', methods=["POST"])
def post_only():
    return "post only page"

在浏览器访问: http://127.0.0.1:5000/post_only

可以看到不允许GET请求

7.3 同一路由装饰多个视图函数

在同一个url路由的请求下,可以会有GET\\POST\\DELETE\\PUT等多个不同的业务处理
那么就需要使用不同的视图函数来区分处理

@app.route('/hello', methods=["GET"])
def hello_get():
    return "hello_get"

@app.route('/hello', methods=["POST"])
def hello_post():
    return "hello_post"
  • 测试访问GET请求 http://127.0.0.1:5000/hello
  • 测试访问POST请求 http://127.0.0.1:5000/hello

    从结果来看,同一个url下,根据配置不同的method,则可以设置到不同的视图函数进行业务处理

7.4 同一视图多个路由装饰器

一个视图如何定义多个url进行访问?
在Django中只要在urls.py中设置多行url定义即可,而在Flask中只需要使用多个路由修饰器进行定义

# 一个视图设置多个URL
@app.route('/diff_url1')
@app.route('/diff_url2')
def diff_url():
    return "diff url"
  • 访问第一个url地址 http://127.0.0.1:5000/diff_url1
  • 访问第二个url地址 http://127.0.0.1:5000/diff_url2

7.5 使用 url_for 进行反解析

在Flask中则可以使用url_for来进行反向解析,而Flask的视图函数不需要设置name,而是直接使用视图函数的函数名即可

from flask import  redirect, url_for

@app.route('/hello', methods=["GET"])
def hello_get():
    return "hello_get"

# 使用url_for进行反向解析
@app.route('/url_for_test')
def url_for_test():
    return redirect(url_for('hello_get'))

测试访问 http://127.0.0.1:5000/url_for_test

可以看到页面自动跳转至hello_get的视图函数中

以上是关于pythonFlask之路由的主要内容,如果未能解决你的问题,请参考以下文章

pythonFlask之蓝图Blueprint

pythonFlask路由系统

postfix邮箱搭建之策略限制(笔记-2.20170809)

计算机网络-网络层-路由算法

pythonFlask之session使用

sql server 永久性备份 临时性备份 有啥区别?