1. 在flask中,如果我们在视图函数中使用data = request.get_json()方法获取数据,那么在客户端发送POST请求时,就需要设置设置正确的Content-Type首部。在ajax()函数中,Content-Type首部使用contentType参数设置,JSON对应的值为'application/json;charset=UTF-8'。
注:当数据以GET方法提交时,会和往常一样以查询字符串的形式附加在请求URL中。在视图函数中,我们可以通过request.args属性获取。而在提交POST请求时,如果你没有指定内容类型为JSON,那么数据会以默认的表单类型(即application/xwww-form-urlencoded)提交,在视图函数中需要从request.form属性中获取数据。
理解为,前段发送时指定了发送的是什么格式的数据类型,那么后端接收时就需要根据对应的数据类型去接收。
JSON.parse() : 将json格式的字符串转化为js对象。
JSON.stringify():将js对象转化为json格式的字符串。
2. 我们学习了使用Flask-WTF扩展提供的CSRFProtect扩展设置全局CSRF保护,但是Web API中的视图并不需要使用CSRF防护,因为Web API并不使用cookie认证用户。我们可以使用csrf.exempt()方法来取消对API蓝本的CSRF保护,它接收蓝本对象作为参数:
from todoism.apis.v1 import api def register_extensions(app): ... csrf.init_app(app) csrf.exempt(api)
3. 添加CORS支持
在介绍CORS(Cross Origin Resource Sharing,跨域资源共享)之前,我们需要先了解一下同源策略(Same origin policy)。出于安全考虑,浏览器会限制从脚本内发起的跨域请求。这里的跨域包括不同域名、不同端口、不同HTTP模式(HTTP、HTTPS等)。比如,从exampleA.com向exampleB.com发起的请求就属于跨域请求。
当API蓝本设置了子域后,假设我们的Web API部署在api.example.com中,而程序部署在www.example.com中,这时从www.example.com向API发起的AJAX请求就会因为同源策略而失败。对于向第三方大范围公开的API,更要考虑支持CORS。
CORS需要同时被浏览器和服务器支持,大多数浏览器都支持CORS,我们只需要在服务器端设置支持CORS。我们可以使用扩展Flask-CORS来为API添加跨域访问支持,先使用Pip进行安装:
pip install flask-cors
因为我们只需要对API蓝本中的路由添加跨域请求支持,所以以Flask-CORS扩展只在蓝本中初始化,传入蓝本对象作为参数:
from flask import Blueprint from flask_cors import CORS api_v1 = Blueprint('api_v1', __name__) CORS(api_v1)
默认情况下,Flask-CORS会为蓝本下的所有路由添加跨域请求支持,并且允许来自任意源的跨域请求。
4. 注册路由
在一个Web应用里,客户端和服务器上的Flask程序的交互可以简单概括为以下几步:
1)用户在浏览器输入URL访问某个资源。
2)Flask接收用户请求并分析请求的URL。
3)为这个URL找到对应的处理函数。
4)执行函数并生成响应,返回给浏览器。
5)浏览器接收并解析响应,将信息显示在页面中。
在上面这些步骤中,大部分都由Flask完成,我们要做的只是建立处理请求的函数,并为其定义对应的URL规则。只需为函数附加app.route()装饰器,并传入URL规则作为参数,我们就可以让URL与函数建立关联。这个过程我们称为注册路由(route),路由负责管理URL和函数之间的映射,而这个函数则被称为视图函数(viewfunction)。
@app.route('/') def index(): return '<h1>Hello, World!</h1>'
route()装饰器的第一个参数是URL规则,用字符串表示,必须以斜杠(/)开始。这里的URL是相对URL(又称为内部URL),即不包含域名的URL。以域名www.helloflask.com为例,“/”对应的是根地址(即www.helloflask.com),如果把URL规则改为“/hello”,则实际的绝对地址(外部地址)是www.helloflask.com/hello。
当URL规则中包含变量时,如果用户访问的URL中没有添加变量,比如/greet,那么Flask在匹配失败后会返回一个404错误响应。一个很常见的行为是在app.route()装饰器里使用defaults参数设置URL变量的默认值,这个参数接收字典作为输入,存储URL变量和默认值的映射。在下面的代码中,我们为greet视图新添加了一个app.route()装饰器,为/greet设置了默认的name值:
@app.route('/greet', defaults={'name': 'Programmer'}) @app.route('/greet/<name>') def greet(name): return '<h1>Hello, %s!</h1>' % name
这时如果用户访问/greet,那么变量name会使用默认值Programmer,视图函数返回<h1>Hello,Programmer!</h1>。
5. Flask命令
除了Flask内置的flask run等命令,我们也可以自定义命令。在虚拟环境安装Flask后,包含许多内置命令的flask脚本就可以使用了。
通过创建任意一个函数,并为其添加app.cli.command()装饰器,我们就可以注册一个flask命令。
@app.cli.command() def hello(): click.echo('Hello, Human!')
函数的名称即为命令名称,这里注册的命令即hello,你可以使用flask hello命令来触发函数。作为替代,你也可以在app.cli.command()装饰器中传入参数来设置命令名称,比如app.cli.command('say-hello')会把命令名称设置为say-hello,完整的命令即flask say-hello。
借助click模块的echo()函数,我们可以在命令行界面输出字符。命令函数的文档字符串则会作为帮助信息显示(flask hello--help)。在命令行下执行flask hello命令就会触发这个hello()函数。
6. Request对象
现在该让Flask的请求对象request出场了,这个请求对象封装了从客户端发来的请求报文,我们能从它获取请求报文中的所有数据。
7. 请求钩子
有时我们需要对请求进行预处理(preprocessing)和后处理(postprocessing),这时可以使用Flask提供的一些请求钩子(Hook),它们可以用来注册在请求处理的不同阶段执行的处理函数(或称为回调函数,即Callback)。这些请求钩子使用装饰器实现,通过程序实例app调用,用法很简单:以before_request钩子(请求之前)为例,当你对一个函数附加了app.before_request装饰器后,就会将这个函数注册为before_request处理函数,每次执行请求前都会触发所有before_request处理函数。
@app.before_request def do_something(): pass # 这里的代码会在每个请求处理前执行
after_request钩子和after_this_request钩子必须接收一个响应类对象作为参数,并且返回同一个或更新后的响应对象。
8. 响应报文
响应报文
常见的HTTP状态码
9. 在Flask中生成响应
响应在Flask中使用Response对象表示,响应报文中的大部分内容由服务器处理,大多数情况下,我们只负责返回主体内容。
根据我们在上一节介绍的内容,Flask会先判断是否可以找到与请求URL相匹配的路由,如果没有则返回404响应。如果找到,则调用对应的视图函数,视图函数的返回值构成了响应报文的主体内容,正确返回时状态码默认为200。Flask会调用make_response()方法将视图函数返回值转换为响应对象。
完整地说,视图函数可以返回最多由三个元素组成的元组:响应主体、状态码、首部字段。其中首部字段可以为字典,或是两元素元组组成的列表。
普通的响应可以只包含主体内容:
@app.route('/hello') def hello(): ... return '<h1>Hello, Flask!</h1>'
默认的状态码为200
指定了不同的状态码:
@app.route('/hello') def hello(): ... return '<h1>Hello, Flask!</h1>', 201
有时你会想附加或修改某个首部字段。比如,要生成状态码为3XX的重定向响应,需要将首部中的Location字段设置为重定向的目标URL:
@app.route('/hello') def hello(): ... return '', 302, {'Location',:'http://www.example.com'}
现在访问http://localhost:5000/hello,会重定向到http://www.example.com。在多数情况下,除了响应主体,其他部分我们通常只需要使用默认值即可。
对于重定向这一类特殊响应,Flask提供了一些辅助函数。除了像前面那样手动生成302响应,我们可以使用Flask提供的redirect()函数来生成重定向响应,重定向的目标URL作为第一个参数。前面的例子可以简化为:
from flask import Flask, redirect # ... @app.route('/hello') def hello(): return redirect('http://www.example.com')
使用redirect()函数时,默认的状态码为302,即临时重定向。
10. 响应格式
在HTTP响应中,数据可以通过多种格式传输。大多数情况下,我们会使用HTML格式,这也是Flask中的默认设置。在特定的情况下,我们也会使用其他格式。不同的响应数据格式需要设置不同的MIME类型,MIME类型在首部的Content-Type字段中定义,以默认的HTML类型为例:
Content-Type: text/html; charset=utf-8
MIME类型(又称为media type或content type)是一种用来标识文件类型的机制,它与文件扩展名相对应,可以让客户端区分不同的内容类型,并执行不同的操作。
如果你想使用其他MIME类型,可以通过Flask提供的make_response()方法生成响应对象,传入响应的主体作为参数,然后使用响应对象的mimetype属性设置MIME类型,比如:
from flask import make_response @app.route('/foo') def foo(): response = make_response('Hello, World!') response.mimetype = 'text/plain' return response
你也可以直接设置首部字段,比如response.headers['Content-Type']='text/xml;charset=utf-8'。但操作mimetype属性更加方便,而且不用设置字符集(charset)选项。
常用的数据格式有纯文本、HTML、XML和JSON,下面我们分别对这几种数据进行简单的介绍和分析。
1.纯文本
MIME类型:text/plain
事实上,其他几种格式本质上都是纯文本。比如同样是一行包含HTML标签的文本“<h1>Hello,Flask!</h1>”,当MIME类型设置为纯文本时,浏览器会以文本形式显示“<h1>Hello,Flask!</h1>”;当MIME类型声明为text/html时,浏览器则会将其作为标题1样式的HTML代码渲染。
2.HTML
MIME类型:text/html
3.XML
MIME类型:application/xml
<?xml version="1.0" encoding="UTF-8"?> <note> <to>Peter</to> <from>Jane</from> <heading>Reminder</heading> <body>Don't forget the party!</body> </note>
4.JSON
MIME类型:application/json
JSON(http://json.org/)指JavaScript Object Notation(JavaScript对象表示法),是一种流行的、轻量的数据交换格式。
Flask通过包装这些方法提供了更方便的jsonify()函数。借助jsonify()函数,我们仅需要传入数据或参数,它会对我们传入的参数进行序列化,转换成JSON字符串作为响应的主体,然后生成一个响应对象,并且设置正确的MIME类型。
jsonify()函数默认生成200响应,你也可以通过附加状态码来自定义响应类型,比如:
@app.route('/foo') def foo(): return jsonify(message='Error!'), 500
11. AJAX相关
ajax()函数支持的参数