Flask路由&视图

发布时间:2019-05-31 21:40:09编辑:auto阅读(2053)

    1 路由

     1.1app.url_map 查看所有路由

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def hello_world():
        """定义视图函数"""
        print(app.url_map)
        return "hello flask"
    
    
    @app.route('/index')
    def index():
        return 'index'
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    View Code

    访问127.0.0.1 :5000

    显示当前所有的路由,和每条路由的访问方式。

    1.2通过method限定访问方式

    给index函数添加method

    @app.route('/index', methods=["post"])
    def index():
        return 'index'
    View Code

    此时去浏览器在地址栏输入地址访问,

    很显然,不让get请求,给method列表中添加get请求方式。

    @app.route('/index', methods=["post", 'get'])
    def index():
        return 'index'
    View Code

    此时就能get和post请求

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

     添加两个视图函数

    @app.route('/test')
    def test1():
        return "test1"
    
    
    @app.route("/test")
    def test2():
        return "test2"
    View Code

    在浏览器访问127.0.0.1:5000/test,返回的是test1.

    修改两个函数的请求方式

    @app.route('/test', methods=["post"])
    def test1():
        return "test1"
    
    
    @app.route("/test", methods=["get"])
    def test2():
        return "test2"
    View Code

    再去浏览器访问127.0.0.1:5000/test,返回的是test2

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

     添加一个func视图函数

    @app.route("/func1")
    @app.route("/func2")
    def func():
        return "func"
    View Code

    在浏览器访问127.0.0.1:5000/func1和 127.0.0.1:5000/func2,返回的结果都是func。

    1.5 使用url_for进行反解析

    添加一个login视图函数

    from flask import Flask, redirect, url_for
    
    @app.route("/login")
    def login():
        url = url_for("index")      # 使用url_for,通过视图函数的名字找到视图对应url
        return redirect(url)
    View Code

    访问127.0.0.1:5000/login,然后就会直接跳转到index页面。

    1.6 动态路由

    # 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的
    @app.route('/book/<int:book_id>')
    def book_detail(book_id):
        return "book detail %s" % book_id
    View Code

    # 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的
    # @app.route('/book/<int:book_id>')
    @app.route('/book/<book_id>')   # 不加转换器类型,默认时普通字符串规则,除了 / 字符
    def book_detail(book_id):
        return "book detail %s" % book_id
    View Code

    1.7 自定义转换器

    from werkzeug.routing import BaseConverter
    
    # 1.定义自己的转换器
    class RegexConverter(BaseConverter):
    
        def __init__(self, url_map, regex):
            # 调用父类的初始化方法
            super().__init__(url_map)
            # 将正则表达式的参数保存到对象的属性中, flask会去使用这个属性来进行路由的正则匹配
            self.regex = regex
    
    
    # 2.将自定义的转换器添加到flask的应用中
    app.url_map.converters["re"] = RegexConverter
    
    # 127.0.0.1:5000/send/178456123789
    @app.route("/send/<re(r'1[34578]\d{9}'):mobile>")
    def send_msg(mobile):
        return "send_msg---To:%s" % mobile
    View Code

    1.8 转换器转换过程

    自定义的转换器RegexConverter添加两个方法,他们都是父类BaseConverter的方法,现在看下他们其实是在做什么。

    to_python

    # 1.定义自己的转换器
    class RegexConverter(BaseConverter):
    
        def __init__(self, url_map, regex):
            # 调用父类的初始化方法
            super().__init__(url_map)
            # 将正则表达式的参数保存到对象的属性中, flask会去使用这个属性来进行路由的正则匹配
            self.regex = regex
    
        def to_python(self, value):
            print(value)
            return 123
    
        def to_url(self, value):
            pass
    
    # 2.将自定义的转换器添加到flask的应用中
    app.url_map.converters["re"] = RegexConverter
    
    # 127.0.0.1:5000/send/178456123789
    @app.route("/send/<re(r'1[34578]\d{9}'):mobile>")
    def send_msg(mobile):
        return "send_msg---To:%s" % mobile
    View Code

    在浏览器访问http://127.0.0.1:5000/send/17845678912,页面显示 

    控制台属输出17845678912。转换过程:

    to_url

    class RegexConverter(BaseConverter):
    
        def __init__(self, url_map, regex):
            # 调用父类的初始化方法
            super().__init__(url_map)
            # 将正则表达式的参数保存到对象的属性中, flask会去使用这个属性来进行路由的正则匹配
            self.regex = regex
    
        def to_python(self, value):
            # print(value)
            # value是在路径进行正则表达式匹配的时候提取的参数
            # return 123
            return value
    
        def to_url(self, value):
            print(value)
            return "15612187594"
    
    # 2.将自定义的转换器添加到flask的应用中
    app.url_map.converters["re"] = RegexConverter
    
    # 127.0.0.1:5000/send/178456123789
    @app.route("/send/<re(r'1[34578]\d{9}'):mobile>")
    def send_msg(mobile):
        return "send_msg---To:%s" % mobile
    
    @app.route("/index")
    def index():
        url = url_for("send_msg", mobile="18732141231")
        return redirect(url)
    View Code

     访问http://127.0.0.1:5000/index, 页面显示:

    控制台输出:18732141231, 转换过程:

    to_url是在使用url_for方法的时候被调用。

     

    2 获取请求参数

    from flask import request

    就是 Flask 中表示当前请求的 request 对象,request对象中保存了一次HTTP请求的一切信息。

    from flask import Flask, request
    
    app = Flask(__name__)
    
    
    @app.route("/index", methods=["get", 'post'])
    def index():
        # request中包含了前端发送过来的所有请求数据
        # form和data是用来提取请求体数据
        # 通过request.form可以直接提取请求体中的表单格式的数据, 是一个类字典的对象
        # 通过get方法只能拿到多个同名参数的第一个
        name = request.form.get("name")
        age = request.form.get("age")
        # getlist可以获取同名的所有数据
        name_list = request.form.getlist("name")
        print("request.data", request.data)     # 发送json数据
    
        # 127.0.0.1:5000/index?page=1 查询字符串 QueryString
        # args使用提取url中的参数(查询字符串)
        page = request.args.get("page")
        print(page)
        return "%s:%s,,%s" % (name, age, name_list)
    
    
    if __name__ == '__main__':
        app.run()
    View Code

    2.1 上传文件 

    已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的 files 属性访问它们。每个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有一个 save() 方法,这个方法允许你把文件保存到服务器的文件系统上。这里是一个用它保存文件的例子:

    @app.route("/upload", methods=['post'])
    def upload():
        # 接收前端发送过来的文件
        file_obj = request.files.get("pic")
        # if file_obj is None:
        #     return "未上传文件"
        # # 将文件保存到本地
        # # 1.创建一个文件对象
        # f = open('./test.png', 'wb')
        # # 2.向文件写内容
        # content = file_obj.read()
        # f.write(content)
        # # 3.关闭文件
        # f.close()
    
        # 直接使用上传文件的对象保存
        file_obj.save("./test1.png")
        return "上传成功"
    View Code

     

    3 abort函数与自定义异常处理

    3.1 abort函数

    from flask import abort
    @app.route("/login", methods=['get'])
    def login():
        name = "hhh"
        pwd = '123'
        if name != "aaa" or pwd != 123:
            # 使用abort函数可以立即终止视图函数的执行, 并可以返回了浏览器特定的信息
            # 1.传递状态码信息, 必须是标准的http状态码
            abort(404)
            # 2.传递响应体信息
            # res = Response("login failed")
            # abort(res)
        return "login success"
    View Code

    3.2 自定义异常处理

    @app.errorhandler(404)
    def handle_error(err):
        """自定义处理错误方法"""
        # 这个函数的返回值会是用户看到的最终结果
        return "首页已换, 下次记得收藏网站。。%s" % err
    View Code

     

    4 返回的响应数据

     4.1 元组

    可以返回一个元组,这样的元组必须是 (response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。

    @app.route("/index")
    def index():
        # 使用元祖返回自定义的响应信息
        #           响应头                      状态码   响应头
        # return "使用元祖返回自定义的响应信息", 400, [("name", "haha")]
        # return "使用元祖返回自定义的响应信息", 400, {("name", "haha")}
        # return "使用元祖返回自定义的响应信息", 666, {("name", "haha")}
        # return "使用元祖返回自定义的响应信息", "666 mystatus", {("name", "haha")}
        # return "使用元祖返回自定义的响应信息", "666 mystatus"
    View Code

    4.2 make_response

    from flask import make_response
    
    
    @app.route("/index")
    def index():
        # 使用make_response来构造响应信息
        res = make_response("使用make_response来构造响应信息")
        res.status = "666 mystatus"     # 设置状态码
        res.headers["name"] = "haha"    # 设置响应头
        return res
    View Code

     

    5 使用jsonify返回json数据

    from flask import jsonify
    
    @app.route("/index")
    def index():
        data = {"title": "santi", "price": 90}
        # return json.dumps(data)     # 此时响应头的Content-Type: text/html; charset=utf-8
        # 手动设置Content-Type
        # return json.dumps(data), 200, {"Content-Type": "application/json"}
        # 使用jsonify, jsonify帮助转换json数据, 并设置响应头Content-Type": "application/json"
        # return jsonify(data)
        return jsonify(title="santi", price=90)
    View Code

     

    6 设置和读取cookie

    from flask import Flask, make_response, request
    
    app = Flask(__name__)
    
    
    @app.route("/set_cookie")
    def set_cookie():
        res = make_response("set_cookie")
        # 默认有效期是临时cookie, 浏览器关闭就失效
        # res.set_cookie("name", "test")
        # max_age设置有效期, 单位:秒
        # res.set_cookie("name1", "test1", max_age=3600)
        # 通过响应头设置cookie
        # res.headers["Set-Cookie"] = "name2=test2; Expires=Sun, 28-Apr-2019 12:37:29 GMT; Max-Age=3600; Path=/"
        return res
    
    
    @app.route("/get_cookie")
    def get_cookie():
        cook = request.cookies.get("name1")
        return cook
    
    
    @app.route("/delete_cookie")
    def delete_cookie():
        res = make_response("delete_cookie")
        res.delete_cookie("name1")
        return res
    
    
    if __name__ == '__main__':
        app.run()
    View Code

     

    7 session

    from flask import session
    
    需要设置secret_key
    from flask import Flask, session
    
    app = Flask(__name__)
    
    app.config["SECRET_KEY"] = "qlxmxlnmj"
    
    @app.route("/login")
    def login():
        # 设置session
        session["name"] = "test"
        return "login success"
    
    @app.route("/index")
    def index():
        # 获取session
        name= session.get("name")
        return "session :: %s" %name
    
    if __name__ == '__main__':
        app.run()
    View Code

     

    8 请求上下文与应用上下文

    上下文:相当于一个容器,保存了Flask程序运行过程中的一些信息。

    Flask中有两种上下文,请求上下文和应用上下文。

    8.1 请求上下文(request context)

    request和session都属于请求上下文对象。

    request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。

    session:用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。

    8.2 应用上下文(application context)

    current_app和g都属于应用上下文对象。

    current_app:表示当前运行程序文件的程序实例。我们可以通过current_app.name打印出当前应用程序实例的名字。

    g:处理请求时,用于临时存储的对象,每次请求都会重设这个变量。比如:我们可以获取一些临时请求的用户信息。

    • 当调用app = Flask(_name_)的时候,创建了程序应用对象app;

    • request 在每次http请求发生时,WSGI server调用Flask.call();然后在Flask内部创建的request对象;

    • app的生命周期大于request和g,一个app存活期间,可能发生多次http请求,所以就会有多个request和g。

    • 最终传入视图函数,通过return、redirect或render_template生成response对象,返回给客户端。

    区别: 请求上下文:保存了客户端和服务器交互的数据。 应用上下文:在flask程序运行过程中,保存的一些配置信息,比如程序文件名、数据库的连接、用户信息等。

     

    9 请求钩子

    在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:在请求开始时,建立数据库连接;在请求结束时,指定数据的交互格式。为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

    请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:

    before_first_request:在处理第一个请求前运行。

    before_request:在每次请求前运行。

    after_request:如果没有未处理的异常抛出,在每次请求后运行。

    teardown_request:在每次请求后运行,即使有未处理的异常抛出。

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route("/index")
    def index():
        print("index 被执行")
        return "index"
    
    
    @app.before_first_request
    def handle_before_first_request():
        """在第一次请求处理之前被执行"""
        print("handle_before_first_request 被执行")
    
    
    @app.before_request
    def handle_before_request():
        """在每次请求之前都被执行"""
        print("handle_before_request 被执行")
    
    
    @app.after_request
    def handle_after_request(response):
        """在每次请求(视图函数处理)之后都被执行, 前提是视图函数没有出现异常"""
        print("handle_after_request 被执行")
        return response
    
    
    @app.teardown_request
    def handle_teardown_request(response):
        """在每次请求(视图函数处理)之后都被执行, 无论视图函数是否出现异常,都被执行"""
        print("handle_teardown_request 被执行")
        return response
    
    
    if __name__ == '__main__':
        app.run()
    View Code

    启动服务器,在浏览器访问index,控制台输出结果:

    服务器不重启,再次刷新浏览器,控制台输出:

    在index函数中添加一个异常,

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route("/index")
    def index():
        print("index 被执行")
        int("A")
        return "index"
    
    
    @app.before_first_request
    def handle_before_first_request():
        """在第一次请求处理之前被执行"""
        print("handle_before_first_request 被执行")
    
    
    @app.before_request
    def handle_before_request():
        """在每次请求之前都被执行"""
        print("handle_before_request 被执行")
    
    
    @app.after_request
    def handle_after_request(response):
        """在每次请求(视图函数处理)之后都被执行, 前提是视图函数没有出现异常"""
        print("handle_after_request 被执行")
        return response
    
    
    @app.teardown_request
    def handle_teardown_request(response):
        """在每次请求(视图函数处理)之后都被执行, 无论视图函数是否出现异常,都被执行,  工作在非调试模式, debug=false"""
        print("handle_teardown_request 被执行")
        return response
    
    
    if __name__ == '__main__':
        app.run()
    View Code

    把调试模式关了, 重启服务器,在浏览器访问,控制台输出:

     

    10 Flask-Script扩展命令行

    通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过python hello.py runserver --host ip地址,告诉服务器在哪个网络接口监听来自客户端的连接。默认情况下,服务器只监听来自服务器所在计算机发起的连接,即localhost连接。

    pip3 install Flask-Script
    from flask import Flask
    from flask_script import Manager
    
    app = Flask(__name__)
    
    # 创建Manager管理类的对象
    manager = Manager(app)
    
    
    @app.route("/index")
    def index():
        return "index"
    
    
    if __name__ == '__main__':
        # app.run()
        # 通过管理对象来启动
        manager.run()
    View Code

     

关键字