flask入门学习

flask是用python编写的轻量级web应用框架。
本文深度参考:https://dormousehole.readthedocs.io/en/latest/quickstart.html#quickstart

1 最简单的flask应用

最简单的flask应用如下述代码所示:

from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    app.run(host="127.0.0.1",port=9000)

在上述代码中:

  • 首先从flask中引入Flask类,使用该类的实例构建了WSGI应用,即app
  • app = Flask(__name__)的含义如1.1节所示;
  • route装饰器确定Flask触发响应函数的url,将url -- /hello/路由到对应的hello函数上;
  • if __name__ == '__main__'在通过其他文件引用这个文件的时候不会执行下面的代码;
  • app.run(host="127.0.0.1",port=9000)表示启动服务,具体参数含义如1.2节所示。

1.1 app = Flask(name)

在python中,__name__可以是__main__,也可以是实际导入的包的名称。假设定义一个test_name.py文件,将其放在/home/xxx/test路径下,使用print(__name__)显示包名。则在不同的路径下运行代码时,输出结果是不一致的。如果以python test.py运行,输出__main__;如果在/home/xxx/test路径下,以import test_name的方式导入代码,则输出test_name;如果在/home/xxx路径下,以from test import test_name的方式导入代码,则输出test.test_name

对于Flask类而言,需要通过传入的参数确定应用的主目录,从而获取其他依赖资源(如静态文件和模板文件)的存放路径。因此,如果使用的是单个模块,传入__name__是正确的选择;如果使用的是包的形式,那么有两种选择,即app = Flask('test')app = Flask(__name__.split('.')[0]),前者是硬编码应用所在的路径,后者是从__name__中获取路径,后者相对实用性更好一些。

在生产环境下,必须设置if __name__ == '__main__',这是因为生产环境下一般会使用nginx+wsgi的形式,wsgi server一般会加载入口文件启动一个服务,如果没有设置if __name__ == '__main__',在服务器上就会启动两个服务,这是不允许的。

1.2 app.run()

本部分转载自:https://waliblog.com/python/2019/07/11/flask-4.html
app.run()的源码如下:

def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
    """Runs the application on a local development server.

    Do not use ``run()`` in a production setting. It is not intended to
    meet security and performance requirements for a production server.
    Instead, see :ref:`deployment` for WSGI server recommendations.

    If the :attr:`debug` flag is set the server will automatically reload
    for code changes and show a debugger in case an exception happened.

    If you want to run the application in debug mode, but disable the
    code execution on the interactive debugger, you can pass
    ``use_evalex=False`` as parameter.  This will keep the debugger's
    traceback screen active, but disable code execution.

    It is not recommended to use this function for development with
    automatic reloading as this is badly supported.  Instead you should
    be using the :command:`flask` command line script's ``run`` support.

    .. admonition:: Keep in Mind

        Flask will suppress any server error with a generic error page
        unless it is in debug mode.  As such to enable just the
        interactive debugger without the code reloading, you have to
        invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
        Setting ``use_debugger`` to ``True`` without being in debug mode
        won't catch any exceptions because there won't be any to
        catch.

    :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
        have the server available externally as well. Defaults to
        ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable
        if present.
    :param port: the port of the webserver. Defaults to ``5000`` or the
        port defined in the ``SERVER_NAME`` config variable if present.
    :param debug: if given, enable or disable debug mode. See
        :attr:`debug`.
    :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
        files to set environment variables. Will also change the working
        directory to the directory containing the first file found.
    :param options: the options to be forwarded to the underlying Werkzeug
        server. See :func:`werkzeug.serving.run_simple` for more
        information.

    .. versionchanged:: 1.0
        If installed, python-dotenv will be used to load environment
        variables from :file:`.env` and :file:`.flaskenv` files.

        If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`
        environment variables will override :attr:`env` and
        :attr:`debug`.

        Threaded mode is enabled by default.

    .. versionchanged:: 0.10
        The default port is now picked from the ``SERVER_NAME``
        variable.
    """
    # Change this into a no-op if the server is invoked from the
    # command line. Have a look at cli.py for more information.
    if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
        from .debughelpers import explain_ignored_app_run

        explain_ignored_app_run()
        return

    if get_load_dotenv(load_dotenv):
        cli.load_dotenv()

        # if set, let env vars override previous values
        if "FLASK_ENV" in os.environ:
            self.env = get_env()
            self.debug = get_debug_flag()
        elif "FLASK_DEBUG" in os.environ:
            self.debug = get_debug_flag()

    # debug passed to method overrides all other sources
    if debug is not None:
        self.debug = bool(debug)

    _host = "127.0.0.1"
    _port = 5000
    server_name = self.config.get("SERVER_NAME")
    sn_host, sn_port = None, None

    if server_name:
        sn_host, _, sn_port = server_name.partition(":")

    host = host or sn_host or _host
    # pick the first value that's not None (0 is allowed)
    port = int(next((p for p in (port, sn_port) if p is not None), _port))

    options.setdefault("use_reloader", self.debug)
    options.setdefault("use_debugger", self.debug)
    options.setdefault("threaded", True)

    cli.show_server_banner(self.env, self.debug, self.name, False)

    from werkzeug.serving import run_simple

    try:
        run_simple(host, port, self, **options)
    finally:
        # reset the first request information if the development server
        # reset normally.  This makes it possible to restart the server
        # without reloader and that stuff from an interactive shell.
        self._got_first_request = False

1.2.1 不能用于生产环境

提示信息很明确的给出了,app.run()不适用于生产环境,安全性和性能难以保证。生产环境下,使用WSGI server。

1.2.2 参数

  • host
    设置处于监听状态的主机名;
    默认是127.0.0.1,表示只能通过本机访问服务,网络内的其他机器不能访问该服务;
    如果设置为本机的IP地址,如192.168.0.216,表示只能通过192.168.0.216:port号来访问服务;
    如果设置为0.0.0.0,表示可以信任网络中的用户,允许服务被公开访问,可以通过本机IP+port号的方式访问服务,也可以通过服务器域名+port号的方式访问服务。

  • port
    设置服务所在的端口号,默认是5000,人为设置时,要避开一些常用服务的端口号,如网页占用的8080mysql占用的3036pgsql5432

  • debug
    设置debug=True表示打开调试模式,默认是关闭调试模式的。

    打开调试模式的作用有两个:
    1.在更改代码后,不需要手动重启服务器,会自动重启;
    2.如果代码运行出错,会返回客户端具体错误原因,方便快速定位bug。如在响应函数中执行print('The Value is: %d' %sss)sss是字符串类型。在未打开调试模式时,客户端收到的返回结果是The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.;打开调试模式后,客户端收到的返回结果是TypeError:%d format:a number is required,not str。两者对比,后者能提供更多的bug定位信息。

    因此,在开发过程中,建议打开调试模式。但绝对不要在生产环境中使用调试器。

  • load_dotenv
    加载最近位置的.env.flaskenv文件来设置环境变量,一般无需设置。

1.2.3 配置文件

项目开发中,可以设置一个配置文件,这样如果产品部署后,可以通过修改配置文件更改相关设置。

文件结构

fisher
|-config.py
|-fisher.py

config.py

HOST='127.0.0.1'
PORT=9000
DEBUG=True

flask读取配置文件:

#coding=utf-8
import os
from flask import Flask,escape

app = Flask(__name__)
app.config.from_object('config')
print(app.config['DEBUG'])

**路由函数**

if __name__ == "__main__":
    app.run(host=app.config['HOST'],port=app.config['PORT'],debug=app.config['DEBUG'])

使用app.config.from_object来装置配置,配置文件中的配置必须是大写,否则会被忽略。

使用app.config.from_object来装置配置是对默认配置参数的修改,默认配置参数为:

#: Default configuration parameters.
default_config = ImmutableDict(
    {
        "ENV": None,
        "DEBUG": None,
        "TESTING": False,
        "PROPAGATE_EXCEPTIONS": None,
        "PRESERVE_CONTEXT_ON_EXCEPTION": None,
        "SECRET_KEY": None,
        "PERMANENT_SESSION_LIFETIME": timedelta(days=31),
        "USE_X_SENDFILE": False,
        "SERVER_NAME": None,
        "APPLICATION_ROOT": "/",
        "SESSION_COOKIE_NAME": "session",
        "SESSION_COOKIE_DOMAIN": None,
        "SESSION_COOKIE_PATH": None,
        "SESSION_COOKIE_HTTPONLY": True,
        "SESSION_COOKIE_SECURE": False,
        "SESSION_COOKIE_SAMESITE": None,
        "SESSION_REFRESH_EACH_REQUEST": True,
        "MAX_CONTENT_LENGTH": None,
        "SEND_FILE_MAX_AGE_DEFAULT": timedelta(hours=12),
        "TRAP_BAD_REQUEST_ERRORS": None,
        "TRAP_HTTP_EXCEPTIONS": False,
        "EXPLAIN_TEMPLATE_LOADING": False,
        "PREFERRED_URL_SCHEME": "http",
        "JSON_AS_ASCII": True,
        "JSON_SORT_KEYS": True,
        "JSONIFY_PRETTYPRINT_REGULAR": False,
        "JSONIFY_MIMETYPE": "application/json",
        "TEMPLATES_AUTO_RELOAD": None,
        "MAX_COOKIE_SIZE": 4093,
    }
)

2 路由函数

使用route装饰器将函数绑定到指定的url

2.1 基本路由函数

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

@app.route("/hello")
def Hello():
    return "Hello World!"

通过访问IP:Port/可以访问到hello函数,通过访问IP:Port/hello可以访问到Hello函数。

2.2 变量规则

通过在url中添加<variable_name>可以在url中添加变量,标记的部分会作为关键字参数传递给函数。如下面代码所示:

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % escape(username)

通过使用<converter:variable_name>,可以选择性的加上一个转换器,为变量指定规则。如下面代码所示:

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % escape(subpath)

show_post函数要求传入的参数为int类型,否则会报找不到对应函数的接口的错误。对应show_subpath函数则要求传入的参数类型为path。各转换器的具体类型为:

名称含义
string(缺省值) 接受任何不包含斜杠的文本
int接受正整数
float接受正浮点数
path类似 string ,但可以包含斜杠
uuid接受 UUID 字符串

2.3 url重定向

访问尾部没有/url时会自动进行重定向,在最后拼接一个/

对下面的代码:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

按照IP:Port/projectsIP:Port/projects/格式,都可以正常响应;

按照IP:Port/about格式,可以正常响应,但是按照IP:Port/about/格式会返回一个404的error。

2.4 url构建

url_for() 函数用于构建指定函数的 URL。它把函数名称作为第一个参数,可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量将添加到 URL 中作为查询参数(什么是查询参数)。

为什么不在把 URL 写死在模板中,而要使用反转函数 url_for() 动态构建?

  • 反转通常比硬编码 URL 的描述性更好。
  • 你可以只在一个地方改变 URL ,而不用到处乱找。
  • URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。
  • 生产的路径总是绝对路径,可以避免相对路径产生副作用。
  • 如果你的应用是放在 URL 根路径之外的地方(如在/myapplication 中,不在 /中), url_for() 会为你妥善处理。

例如,这里我们使用 test_request_context() 方法来尝试使用 url_for()test_request_context()告诉 Flask 正在处理一个请求,而实际上也许我们正处在交互 Python shell 之中, 并没有真正的请求。

from flask import Flask, escape, url_for

app = Flask(__name__)

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

@app.route('/login')
def login():
    return 'login'

@app.route('/user/<username>')
def profile(username):
    return '{}\'s profile'.format(escape(username))

with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))

输出:

/
/login
/login?next=/
/user/John%20Doe

3 HTTP方法及flask实现

3.1 HTTP方法

本节内容引用自:https://blog.csdn.net/vikeyyyy/article/details/80655115
web使用不同的方法来处理url,常见的方法有:

  • GET
  • HEAD
  • PUT
  • POST
  • TRACE
  • OPTIONS
  • DELETE
  • 自定义方法

GET
GET是最常用的方法,通常用于请求服务器发送某个资源。HTTP/1.1 要求服务器实现该方法。下图为客户端用GET方法发起一次HTTP请求。

在这里插入图片描述
HEAD
HEAD方法和GET方法的行为很类似,但是服务器在响应中只返回首部,不会返回实体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查。

使用HEAD方法有以下优点:

  • 在不获取资源的情况下了解资源的情况(比如:判断其类型);
  • 通过查看响应的状态码,看看某个对象是否存在;
  • 通过查看首部,测试资源是否被修改了。

为了确保请求返回的首部与GET请求所返回的首部完全相同,遵循HTTP/1.1规范,就必须实现HEAD方法。

下图为HEAD方法的实际用法:
在这里插入图片描述
PUT
与GET从服务器读取文档想反,PUT方法会向服务器写入文档。

PUT方法的语义就是让服务器用请求的主体部分来创建一个由所请求的URL命名的新文档,或者替换已存在的URL。具体使用方法如下:

在这里插入图片描述
POST
POST方法期初是用来向服务器输入数据的,实际上,通常会用它来支持HTML的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到它要去的地方。

下图为POST方法发起HTTP请求发送表单数据的示例图:
在这里插入图片描述
TRACE
客户端发起一个请求时,这个请求可能要穿过防火墙、代理、网关或者其他一些应用程序。每个中间节点都可能会修改原始的HTTP请求。TRACE方法允许客户端在最终将请求发送给服务器时,看看它变成什么样子了。

TRACE请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文。这样客户端就可以查看在所有中间HTTP应用程序组成的请求/响应链上,原始报文是否被毁坏,以及如何被毁坏或修改过。

具体示例如下:
在这里插入图片描述TRACE方法主要用于诊断,用于验证请求是否如愿穿过了请求/响应链。可以用来查看代理和其他应用程序对用户请求所产生的效果。

TRACE的缺点是:它假定中间应用程序对各种不同类型请求的处理是相同的。很多HTTP应用程序会根据方法的不同做出不同的事情,例如代理会把POST请求直接发给服务器,而GET请求发送给另一个HTTP应用改程序(比如缓存)。TRACE不提供区分这些方法的机制,通常中间应用程序会自行决定对TRACE请求的处理方式。

TRACE请求中不能带有实体的主体部分。TRACE响应的实体主体部分包含了响应服务器收到的请求的精确副本。

OPTIONS
OPTIONS方法请求Web服务器告知其支持的各种功能。可以询问服务器通常支持哪些方法,或者对某些特殊资源支撑哪些方法。(有些服务器可能只支持对一些特殊类型的对象使用特定的操作)

这为客户端应用程序提供了一种手段,使其不用实际访问那些资源就能判定访问各种资源的最优方式。

下图为使用OPTIONS方法的请求:
在这里插入图片描述
DELETE
DELETE方法所做的事情就是请服务器删除请求URL所指定的资源。但客户端应用程序无法保证删除操作一定会被执行。因为HTTP规范允许服务器在不通知客户端的情况下撤销请求。

在这里插入图片描述
自定义方法
http被设计成可扩展的,这样新特性就不会使老的版本失效。扩展方法指的是没在HTTP/1.1中规范定义的,服务器为它管理的资源实现一些HTTP服务,为开发者提供了一种扩展这些HTTP服务能力的手段。

下表列出了一些常用的扩展方法:

  • LOCK:允许用户“锁定资源”编辑某个资源时将其锁定,以防别人同时对其修改;
  • MKCOL:允许用户创建资源;
  • COPY:便于在服务器上复制资源;
  • MOVE:在服务器上移动资源

上面例子中的方法便于用过http将web内容发布到web服务器上。

不是所有的扩展都是在正式规范中定义的,因此,扩展方法秉持“对发送内容要求严格,对所接受的内容宽容点”来处理一般的HTTP扩展方法很重要。

GET和POST的区别
1、GET请求的数据是放在HTTP包头中的,也就是URL之后,通常是像下面这样定义格式的:
login.action?name=hyddd&password=idontknow&verify=%E4%BD%E5%A5%BD
其中,以?来分隔URL和数据;以&来分隔参数;如果数据是英文或数字,原样发送;如果数据是中文或其它字符,则进行BASE64编码。

而Post是把提交的数据放在HTTP正文中的

2、GET提交的数据比较少,最多1024B,因为GET数据是附在URL之后的,而URL则会受到不同环境的限制的,比如说IE对其限制为2K+35,而POST可以传送更多的数据(理论上是没有限制的,但一般也会受不同的环境,如浏览器、操作系统、服务器处理能力等限制,IIS4可支持80KB,IIS5可支持100KB)。

3、Post的安全性要比Get高,因为Get时,参数数据是明文传输的,参数直接暴露在url中,所以不能用来传递敏感信息。而且使用GET的话,还可能造成Cross-site request forgery攻击。而POST数据则可以加密的,但GET的速度可能会快些。

4、get请求只能进行url编码,而post支持多种编码方式;get请求会浏览器主动cache,而post支持多种编码方式;get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。

5、GET和POST本质上都是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

6、GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

3.2 flask中的HTTP方法

缺省情况下,一个路由只回应GET请求。 可以使用route()装饰器的methods参数来处理不同的 HTTP 方法:

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

如果当前使用了 GET 方法, Flask 会自动添加 HEAD 方法支持,并且同时还会 按照 HTTP RFC 来处理 HEAD 请求。同样, OPTIONS 也会自动实现。

4 操作请求数据

对于 web 应用来说对客户端向服务器发送的数据作出响应很重要。在 Flask 中由全局对象request来提供请求信息。

多个url共用一个全局的request,如何保证该request对不同的url是安全的,这是因为request并不是通常意义下的全局对象,而是本地对象(某指定线程下)的全局对象。

设想现在处于处理线程的环境中。一个请求进来了,服务器决定生成一个新线程(或者叫其他什么名称的东西,这个下层的东西能够处理包括线程在内的并发系统)。当 Flask 开始其内部请求处理时会把当前线程作为活动环境,并把当前应用和 WSGI 环境绑定到这个环境(线程)。它以一种聪明的方式使得一个应用可以在不中断的情况下调用另一个 应用。

这对你有什么用?基本上你可以完全不必理会。这个只有在做单元测试时才有用。在测试 时会遇到由于没有请求对象而导致依赖于请求的代码会突然崩溃的情况。对策是自己创建 一个请求对象并绑定到环境。最简单的单元测试解决方案是使用 test_request_context() 环境管理器。通过使用 with 语句 可以绑定一个测试请求,以便于交互。例如:

from flask import request

with app.test_request_context('/hello', method='POST'):
    # now you can do something with the request until the
    # end of the with block, such as basic assertions:
    assert request.path == '/hello'
    assert request.method == 'POST'

另一种方式是把整个 WSGI 环境传递给 request_context() 方法:

from flask import request

with app.request_context(environ):
    assert request.method == 'POST'

4.1 请求对象

参考request的官方文档:https://dormousehole.readthedocs.io/en/latest/api.html#flask.Request

常用的属性有argsformfiles等。args中是以dict形式存储的参数值,form中是以dict形式存储的表单数据,files中是上传的文件的信息。

form

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

当 form 属性中不存在这个键时会发生什么?会引发一个 KeyError 。 如果你不像捕捉一个标准错误一样捕捉 KeyError ,那么会显示一个 HTTP 400 Bad Request 错误页面。

args

URL (如 ?key=value )中提交的参数可以使用 args 属性:

searchword = request.args.get('key', '')

推荐使用 get 或通过捕捉 KeyError 来访问 URL 参数。

files
上传文件时要确保HTML 表单中设置enctype="multipart/form-data"属性。

已上传的文件被储存在内存或文件系统的临时位置。你可以通过请求对象 files 属性来访问上传的文件。每个上传的文件都储存在这个 字典型属性中。这个属性基本和标准 Python file 对象一样,可以使用save函数将上传文件保存到服务器的文件系统中。

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file'] #得到一个python file对象
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果想要知道文件上传之前其在客户端系统中的名称,可以使用 filename 属性。但是请牢记这个值是 可以伪造的,永远不要信任这个值。如果想要把客户端的文件名作为服务器上的文件名, 可以通过 Werkzeug 提供的 secure_filename() 函数:

from flask import request
from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

5 重定向和错误

5.1 重定向

使用redirect函数可以重定向,使用abort可以提前退出。如下面的代码所示:

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

上例实际上是没有意义的,它让一个用户从索引页重定向到一个无法访问的页面(401 表示禁止访问)。但是上例可以说明重定向和出错跳出是如何工作的。

5.2 错误处理

5.2.1 HTTP状态码

参考:https://www.runoob.com/http/http-status-codes.html

下面是常见的HTTP状态码:

  • 200 - 请求成功;
  • 301 - 资源(网页等)被永久转移到其它URL;
  • 404 - 请求的资源(网页等)不存在;
  • 500 - 内部服务器错误。

5.2.2 错误处理

参考自官方文档:https://dormousehole.readthedocs.io/en/latest/errorhandling.html#error-handlers

注册处理器

对于HTTP默认的错误码,可以使用 errorhandler() 装饰函数或使用 register_error_handler() 来注册处理函数,如下面的代码设置了对400错误码的处理函数,即当发生400错误时,返回指定的bad request!信息。

记得当返回响应的时候设置出错代码:

@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e):
    return 'bad request!', 400

# or, without the decorator
app.register_error_handler(400, handle_bad_request)

对于非标准的HTTP错误码,它们不能被注册。需要使用适当的代码定义一个 HTTPException 子类,注册并抛出异常类:

#自定义异常处理类
class InsufficientStorage(werkzeug.exceptions.HTTPException):
    code = 507
    description = 'Not enough storage space.'
    
#抛出异常时,调用下面的代码
raise InsufficientStorage()

异常处理顺序
在处理请求时,当 Flask 捕捉到一个异常时,它首先根据代码检索。如果该代码没有注册处理器,它会根据类的继承来查找,确定最合适的注册处理器。如果找不到已注册的处理器,那么 HTTPException 子类会显示一个关于代码的通用消息。没有代码的异常会被转化为一个通用的 500 内部服务器错误

6 日志

参考自:https://dormousehole.readthedocs.io/en/latest/logging.html#logging

日志使用实例

from flask import Flask
import logging

app = Flask(__name__)
logging_format = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')

handler = logging.FileHandler('flask_new.log')
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging_format)
app.logger.addHandler(handler)

@app.route('/')
def root():
    app.logger.info('info log')
    app.logger.warning('warning log')
    return 'hello'

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

logging.FileHandler('flask_new.log')创建日志文件,函数定义为:
def __init__(self, filename, mode='a', encoding=None, delay=False):
可以看出,第一个参数为日志文件名;
第二参数mode默认为append方式;
第三个参数为编码格式,如果写入中文,需要设置为utf-8

logging的级别主要有NOTSETDEBUGINFOWARNINGERRORCRITICAL

handler.setLevel(logging.DEBUG)即设置日志记录最低级别为DEBUG,低于DEBUG级别的日志记录会被忽略,不设置setLevel()则默认为NOTSET级别。需要注意的是app.logger继承自RootLogger,而RootLogger默认的levelWARNING,因此低于WARNING级别的日志信息会被忽视,某些情况下发现单纯设置app.logger.setLevel无效,需要通过logging.getLogger().setLevel()设置RootLogger的日志级别才能生效。

日志记录格式:

  • %(asctime)s 即日志记录时间,精确到毫秒;
  • %(levelname)s 即此条日志级别;
  • %(filename)s 即触发日志记录的python文件名;
  • %(funcName)s 即触发日志记录的函数名;
  • %(lineno)s 即触发日志记录代码的行号;
  • %(message)s 这项即调用如app.logger.info('info log')中的参数,即message。

7 返回信息

url绑定函数的返回值会自动转换为一个响应对象。如果返回值是一个字符串,那么会被转换为一个包含作为响应体的字符串、一个 200 OK 状态码 和一个 text/html 类型的响应对象。如果返回值是一个字典,那么会调用 jsonify() 来产生一个响应。

以下是转换的规则:

  • 如果视图返回的是一个响应对象,那么就直接返回它;
  • 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的响应对象;
  • 如果返回的是一个字典,那么调用jsonify创建一个响应对象;
  • 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少包含一个项目,且项目应当由(response, status)(response, headers) 或者 (response, status, headers)组成。 status 的值会重载状态码, headers 是一个由额外头部值组成的列表或字典;
  • 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。

可以使用make_response函数包裹返回表达式,获得响应对象,并对该对象进行修改,然后再返回,如下所示:

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

但要注意make_response只能包裹string, tuple, Response instance, or WSGI callable,dict需要使用jsonify进行包裹。

参考:
flask官方文档
flask快速入门

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值