如何flask 自定义过滤器Flask中的响应类

under , , .
The Flask response class, appropriately called Response, is rarely used directly by Flask applications. Instead, Flask uses it under the covers as a container for the response data returned by application route functions, plus some additional information needed to create an HTTP response.
What's not widely known, is that Flask gives applications the option to replace the stock response class with a custom one, opening the door to some neat tricks. In this article I'm going to show you how to take advantage of this technique to simplify your application code.
How Do Flask Responses Work?
Most applications do not use the Response class from Flask directly, but that does not mean it does not get used. In fact, Flask creates response objects for every request. So how does this work?
The response cycle begins when the function invoked by Flask to handle a request returns. In a web application, routes typically end with a call to the render_template function, which causes the referenced template file to be rendered and returned as a string:
@app.route('/index')
def index():
return render_template('index.html')
But as you probably know, a Flask route handler function can optionally return two additional values, which set a response status code and custom HTTP headers:
@app.route('/data')
def index():
return render_template('data.json'), 201, {'Content-Type': 'application/json'}
In this example, the status code is set to 201 to override the Flask 200 default, the standard code for a successfully handled request. The example also sets the Content-Type header to indicate that the response contains JSON data, because if you don't specifically set a content type, Flask sets it to HTML.
The above examples show the three basic components of a response, which are the data or body, the status code, and the headers. Flask's application instance has a make_response() function, which takes the return value from the route function (which can be a single value or a tuple with one, two or three values) and populates a Response object with them.
You can actually see how this process works in a Python console session. First create a virtual environment and install Flask in it, then start a Python session and type the following:
&&& from flask import Flask
&&& app = Flask(__name__)
&&& app.make_response('Hello, World')
&Response 12 bytes [200 OK]&
&&& app.make_response(('Hello, World', 201))
&Response 12 bytes [201 CREATED]&
Here I created a scratch Flask application instance, and then invoked the make_response() method to create some Response objects. In the first one I sent a string as argument, so the response is populated with defaults for status code and headers. In the second example I sent a tuple with two values, to force a non-default status code. Note the double parentheses in this second call, which wrap the string and the status code in a tuple. This is required because make_response() takes a single argument.
Once Flask has a Response object that represents the route function's response, it does a few things with it. Among them, it passes it to any defined after_request handlers, which gives the application a chance to insert or modify headers, change the body or the status code, or even replace the response with a completely new one if it so desires. In the end, Flask takes the final response object, renders it as HTTP, and sends it to the client.
The Flask Response Class
Let's look at the most interesting aspects of the response class. The following class definition shows what in my opinion are the salient attributes and methods of this class:
class Response:
charset = 'utf-8'
default_status = 200
default_mimetype = 'text/html'
def __init__(self, response=None, status=None, headers=None,
mimetype=None, content_type=None, direct_passthrough=False):
@classmethod
def force_type(cls, response, environ=None):
Note that if you go to the Flask source code you will not find the definitions above. Flask's Response class is actually a very tiny shell that derives from Werkzeug's class of the same name. In turn, Werzeug's Response class inherits from the BaseResponse class, in which the above elements are defined.
The three class attributes charset, default_status and default_mimetype define some defaults. If any of these defaults do not work for your application, then you can subclass the Response class and define your own, instead of having to set your custom values in every response. For example, if your application is an API that returns XML from all routes, you can change default_mimetype to application/xml in your custom response and then Flask will return XML responses by default, as you will see later.
I will not go into great detail describing the __init__ constructor (you can read about that in the ), but note that the three important elements of the Flask responses, which are the response body, the status code and the headers, are given as arguments. In a subclass, the constructor could change the rules that determine how responses are created.
The force_type() class method is the only non-trivial, yet very important, element of the response class. There are situations where Werkzeug or Flask need to create their own response objects, such as when an application error occurs and an error response needs to be given to the client. In this case the response object does not come from the application, it is the framework that creates it. In an application that uses a custom response class, Flask and Werkzeug can't possibly know the details of that custom class, so they create their responses using the standard response class. The force_type() method in a response class is supposed to take an instance of a different response class, and convert it to its own format.
I'm pretty sure you are confused by the description of force_type(). The bottom line is that whenever Flask comes across a response object that is not of the expected class, this method is used to convert it. The third use case that I'm going to show below takes advantage of this to allow Flask route functions to return objects such as dictionaries, lists or actually any custom object as a response to a request.
Well, that's enough theory. In the following section I'm going to show you how to put all these Response tricks into practice. Ready to get your hands dirty?
Using a Custom Response Class
By now I'm sure you agree that there are some interesting use cases that can benefit from using a custom response class. Before I show you some actual examples, let me tell you how simple it is to configure a Flask application to use a custom response class. Take a look at the following example:
from flask import Flask, Response
class MyResponse(Response):
app = Flask(__name__)
app.response_class = MyResponse
Here I have defined my custom response class with name MyResponse. Typically a custom response class adds or changes the behavior of the default response class, so it is common to create these custom classes as subclasses of Flask's Response class. To tell Flask to use my custom response class, all I need to do is set my class in app.response_class.
The response_class attribute of class Flask is a class attribute, so as a variant of the above example, you can create a subclass of Flask that has your response class set in it:
from flask import Flask, Response
class MyResponse(Response):
class MyFlask(Flask)
response_class = MyResponse
app = MyFlask(__name__)
Example #1: Changing Response Defaults
The first example is extremely simple. Let's say your application returns XML in most or all endpoints. For such an application it would make sense to make application/xml the default content type. This is easily achieved with a two-line response class:
class MyResponse(Response):
default_mimetype = 'application/xml'
Easy, right? If you set this class as the default response for the application, then you can write functions that return XML without worrying about setting the content type. For example:
@app.route('/data')
def get_data():
return '''&?xml version="1.0" encoding="UTF-8"?&
&name&John Smith&/name&
Using the default response, this route would receive the text/html content type, as that is the default. The custom response saves you from adding the additional header to all return statements in XML routes. And if you have some routes that need a different content type, you can still override the default, like you do with the regular response class:
@app.route('/')
def index():
return '&h1&Hello, World!&/h1&', {'Content-Type': 'text/html'}
Example #2: Determining Content Type Automatically
The next example is a little bit more involved. Let's say that this application has HTML and XML routes in equal amounts, so the first example does not really make sense, since regardless of which default you pick you would still have about half of the routes with an overriden content type.
A better solution would be to create a response class that can determine the correct content type by looking at the response text. The following class does that job:
class MyResponse(Response):
def __init__(self, response, **kwargs):
if 'mimetype' not in kwargs and 'contenttype' not in kwargs:
if response.startswith('&?xml'):
kwargs['mimetype'] = 'application/xml'
return super(MyResponse, self).__init__(response, **kwargs)
In this example I first make sure the response does not specify an explicit content type. I then check if the text of the response begins with &?xml, indicating the data is formatted as an XML document. If both conditions are true, I insert the XML content type in the arguments I sent to the parent's class constructor.
With this response class, any properly formatted XML document will automatically receive the XML content type, while other responses will continue to receive the default content type. And in all cases I'm still free to explicitly provide the content type when necessary.
Example #3: Automatic JSON Responses
The final example deals with a common nuisance in Flask based APIs. It is very common for APIs to return JSON payloads, which requires you to use the jsonify() function to convert a Python dictionary to its JSON representation, and also to set the JSON content type in the response. Here is an example route that does this:
@app.route('/data')
def get_data():
return jsonify({'foo': 'bar'})
Unfortunately every route that returns JSON needs to do this, so for a large API you end up repeating the jsonify() call a lot. From a code readability point of view, wouldn't it be nice if you could do this instead?
@app.route('/data')
def get_data():
return {'foo': 'bar'}
Here is a custom response class that supports the above syntax, without affecting how other routes that do not return JSON work in any way:
class MyResponse(Response):
@classmethod
def force_type(cls, rv, environ=None):
if isinstance(rv, dict):
rv = jsonify(rv)
return super(MyResponse, cls).force_type(rv, environ)
This example requires a bit of explanation. Flask recognizes just a handful of types as valid things that can be returned from a route handler function as a response body. Basically you can return any of the string and binary related types (str, unicode, bytes, bytearray), or if you prefer, you can return an already built response object. If you return any of the string or bytes types, Flask is going to realize that these are types that the response class knows how to handle, so it will pass what you return directly into the constructor of the response class.
But what happens when you return something unsupported, such as the dictionary of the example above? If the type of the returned response is not one of the expected ones, then Flask is going to assume that it is an unknown response object, so it will not try to create a response object passing it as an argument. Instead, it will use the force_type() class method on the response class to force a conversion from the unknown type. In this example the response subclass overrides this method and applies a conversion when a dictionary is given as a response. The conversion is done with a call jsonify(), before letting the base class take over as if nothing happened.
It's a neat trick, right? Consider that this does not affect the normal functioning of other responses. For any routes that return the normal response types the subclass does not do anything, all calls are passed transparently to the parent class.
Conclusion
I hope this article sheds some light on how responses work in Flask. If you know of other tricks you can do with Flask responses I would love to hear them!
10 comments
Leave a CommentFlask|LOFTER(乐乎) - 记录生活,发现同好
LOFTER for ipad —— 记录生活,发现同好
&nbsp&nbsp被喜欢
&nbsp&nbsp被喜欢
{list posts as post}
{if post.type==1 || post.type == 5}
{if !!post.title}${post.title|escape}{/if}
{if !!post.digest}${post.digest}{/if}
{if post.type==2}
{if post.type == 3}
{if !!post.image}
{if post.type == 4}
{if !!post.image}
{if !!photo.labels && photo.labels.length>0}
{var wrapwidth = photo.ow < 500?photo.ow:500}
{list photo.labels as labs}
{var lbtxtwidth = Math.floor(wrapwidth*(labs.ort==1?labs.x:(100-labs.x))/100)-62}
{if lbtxtwidth>12}
{if !!labs.icon}
{list photos as photo}
{if photo_index==0}{break}{/if}
品牌${make||'-'}
型号${model||'-'}
焦距${focalLength||'-'}
光圈${apertureValue||'-'}
快门速度${exposureTime||'-'}
ISO${isoSpeedRatings||'-'}
曝光补偿${exposureBiasValue||'-'}
镜头${lens||'-'}
{if data.msgRank == 1}{/if}
{if data.askSetting == 1}{/if}
{if defined('posts')&&posts.length>0}
{list posts as post}
{if post_index < 3}
{if post.type == 1 || post.type == 5}
{if !!post.title}${post.title|escape}{/if}
{if !!post.digest}${post.digest}{/if}
{if post.type == 2}
{if post.type == 3}
{if post.type == 4}
{if drlist.length>0}
更多相似达人:
{list drlist as dr}{if drlist.length === 3 && dr_index === 0}、{/if}{if drlist.length === 3 && dr_index === 1}、{/if}{if drlist.length === 2 && dr_index === 0}、{/if}{/list}
暂无相似达人,
{if defined('posts')&&posts.length>0}
{list posts as post}
{if post.type == 2}
{if post.type == 3}
{if post.type == 4}
this.p={ currentPage:1,pageNewMode:true,isgooglead3:false,ishotrecompost:false,visitorId:0, first:'',tag:'Flask',recommType:'new',recommenderRole:0,offset:20,type:0,isUserEditor:0,};Flask Web开发学习笔记(三) - 简书
下载简书移动应用
写了26497字,被193人关注,获得了96个喜欢
Flask Web开发学习笔记(三)
本文主要整理下几个Flask扩展:
0.Flask-Bootstrap:集成Twitter开发的一个开源框架Bootstrap。1.Flask-Script:为Flask程序添加一个命令行解析器2.Flask-Moment:本地化日期和时间
本篇基于前两篇博文,需要使用虚拟环境、使用pip安装flask等。
0.什么是扩展
Flask被设计为可扩展形式,故而没有提供一些重要的功能,例如数据库和用户认证,所以我们可以选择最适合的程序的包,或者按需求自行开发。
说白了扩展就是一些附加功能,独立完成某种功能。
1.Flask-Bootstrap
Bootstrap 是
Twitter 开发的一个开源框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,而且这些网页还能兼容所有现代 Web 浏览器。
Bootstrap是客户端框架,因此不会直接涉及服务器。
服务器需要做的指示提供引用了 Bootstrap 层叠样式表(CSS)和 Javascript 文件的 HTML 响应,并在 HTML、CSS 和 JavaScript 代码中实例化所需组件。这些操作最理想的执行场所就是模板。
0.安装Flask-Bootstrap扩展
pip install flask-bootstrap
1.创建app.py文件
app.py文件中写入:
from flask import Flask,render_template
from flask.ext.bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)#注意这个地方
@app.route('/')
def index():
return render_template('index.html')
if __name__ == "__main__":
app.run(debug = True)
0.from flask.ext.bootstrap import Bootstrap:专为 Flask 开发的扩展都暴露在 flask.ext 命名空间下,Flask-Bootstrap 输出了一个 Bootstrap 类。
1.bootstrap = Bootstrap(app):Flask 扩展一般都在创建程序实例时初始化,这行代码是 Flask-Bootstrap 的初始化方法。
2.创建templates文件夹并创建base.html
base.html中写入:
{% extends "bootstrap/base.html"
{% block title %}Flask{% endblock %}
{% block navbar %}
&div class="navbar navbar-inverse" role="navigation"&
&div class="container"&
&div class="navbar-header"&
&button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"&
&span class="sr-only"&Toggle navigation&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&a class="navbar-brand" href="/"&Flasky&/a&
&div class="navbar-collapse collapse"&
&ul class="nav navbar-nav"&
&li&&a href="/"&Home&/a&&/li&
{% endblock %}
{% block content %}
&div class="container"&
{% block page_content %}{% endblock %}
{% endblock %}
0.{% extends "bootstrap/base.html"
%}:base.html模板继承自bootstrap/base.html
1.其中title、navbar、content都是bootstrap/base.html中定义的块(还有script等)。navbar是显示导航栏。其中的代码比较多,作用是添加Flasky和Home两个链接,理解下即可。
2.page_content块是自己加的。
3.以后的html页面直接继承base.html就可以啦。
3.创建index.html
index.html中写入:
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block page_content %}
&h1&这里是首页,Hello World!&/h1&
{% endblock %}
0.{% extends "base.html" %}:继承自base.html
1.page_content块就是上面的自己定义的块。
4.运行程序
显示如下:
运行结果.png
2.Flask-Script
Flask的开发Web服务器支持很多启动设置选项,但只能在脚本中作为参数传给app.run()函数。这种方式很不方便,传递设置选项的理想方式是使用命令行参数。
Flask-Scrip就是这么一个Flask扩展,为Flask程序添加一个命令行解析器。Flask-Script自带了一组常用选项,而且还支持自定义命令。
0.安装Flask-Script扩展
pip install flask-script
1.修改app.py文件
from flask import Flask,render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.script import Manager
app = Flask(__name__)
manager = Manager(app)#注意这个地方
@app.route('/')
def index():
return render_template('index.html')
if __name__ == "__main__":
manager.run()#注意这个地方
0.from flask.ext.script import Manager:Flask-Script输出了一个名为Manager的类,同样也是***flask.ext.XXX***下。
1.manager.run()代替了app.run(),启动后就能解析命令行啦。
2.使用Flask-Script
0.运行app.py:python app.py
3.Flask-Moment
服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般使用协调世界时间(Coordinated Universal Time,UTC)。用户不愿意看到UTC时间,他们更希望看到当地时间,而且采用当地惯用的格式。
要想在服务器上只使用UTC时间,一个优雅的解决方案是,把时间单位发送给Web浏览器。转换成当地时间,然后渲染。Web浏览器可以更好的完成这一任务,因为它能获取用户电脑中的时区和区域设置。
有一个使用JavaScript开发的优秀客户端开源代码,名为moment.js(),它可以在浏览器中渲染日期和时间。
0.安装Flask-Moment扩展
pip install flask-moment
1.修改app.py文件
from flask import Flask,render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.script import Manager
from flask.ext.moment import Moment
from datetime import datetime
app = Flask(__name__)
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
@app.route('/')
def index():
return render_template('index.html',current_time=datetime.utcnow())
if __name__ == "__main__":
manager.run()
0.from flask.ext.moment import Moment:导入一个Moment类
1.from datetime import datetime:导入一个datetime类,用于获取UTC时间
2.moment = Moment(app):向模板开放了moment类,可以在模板中使用
3.current_time=datetime.utcnow():获取当前UTC时间,作为参数传入index.html
2.修改base.html
base.html修改为:
{% extends "bootstrap/base.html"
{% block title %}Flask{% endblock %}
{% block navbar %}
&div class="navbar navbar-inverse" role="navigation"&
&div class="container"&
&div class="navbar-header"&
&button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"&
&span class="sr-only"&Toggle navigation&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&a class="navbar-brand" href="/"&Flasky&/a&
&div class="navbar-collapse collapse"&
&ul class="nav navbar-nav"&
&li&&a href="/"&Home&/a&&/li&
{% endblock %}
{% block content %}
&div class="container"&
{% block page_content %}{% endblock %}
&!--注意下方block script--&
{% block scripts %}
super() }}
{{ moment.include_moment() }}
{% endblock %}
{% endblock %}
0.除了moment.js,Flask-Moment还依赖jquery.js。但是Bootstrap中已经引入了jquery.js,故只需要引入moment.js
1.{{ moment.include_moment() }}:引入moment.js,请注意上方的{{
super() }}
3.修改index.html
index.html内容修改为如下:
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block page_content %}
&h1&这里是首页,Hello World!&/h1&
&p&{{ moment(current_time).format('LLL') }}&/p&
&p&{{ moment(current_time).fromNow(refresh=True) }}&/p&
{% endblock %}
0.{{ moment(current_time).format('LLL') }}:根据客户端电脑中的时区和区域设置渲染日期和时间。参数决定了渲染的方式,'L'到'LLL'分别对应不同的复杂度。format()函数还可以接受自定义的格式说明符。
1.{{ moment(current_time).fromNow(refresh=True) }}:moment是app.py开放的类,current_time是app.py传递过来的参数,fromNow()渲染相对时间戳,参数refresh=True会随着时间的推移自动刷新显示时间。
4.一个完整的Demo
这个Demo,包含上方未介绍到的东西:
0.url_for()函数的使用1.静态文件的引入:设置网页的小图标(如百度网页的那个小熊掌)2.自定义404错误页面
0.创建文件夹,包含文件
文件夹结构如下:
--templates
--base.html
--index.html
--user.html
--404.html
--favicon.png
from flask import Flask,render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.script import Manager
from flask.ext.moment import Moment
from datetime import datetime
app = Flask(__name__)
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
@app.route('/')
def index():
return render_template('index.html',current_time=datetime.utcnow())
@app.route('/user/&name&')
def user(name):
return render_template('user.html',name=name)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404
if __name__ == "__main__":
manager.run()
2.base.html
{% extends "bootstrap/base.html"
{% block head %}
{{ super() }}
&link rel="shortcut icon" href="{{ url_for('static',filename = 'favicon.png') }}"
type='image/x-icon'/&
&link rel="icon" href="{{ url_for('static',filename = 'favicon.png')
}}" type="image/x-icon"/&
{% endblock %}
{% block title %}Flask{% endblock %}
{% block navbar %}
&div class="navbar navbar-inverse" role="navigation"&
&div class="container"&
&div class="navbar-header"&
&button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"&
&span class="sr-only"&Toggle navigation&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&span class="icon-bar"&&/span&
&a class="navbar-brand" href="/"&Flasky&/a&
&div class="navbar-collapse collapse"&
&ul class="nav navbar-nav"&
&li&&a href="/"&Home&/a&&/li&
{% endblock %}
{% block content %}
&div class="container"&
{% block page_content %}{% endblock %}
{% block scripts %}
super() }}
{{ moment.include_moment() }}
{% endblock %}
{% endblock %}
3.index.html
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block page_content %}
&h1&这里是首页,Hello World!&/h1&
&p&{{ moment(current_time).format('LLL') }}&/p&
&p&{{ moment(current_time).fromNow(refresh=True) }}&/p&
{% endblock %}
4.user.html
{% extends "base.html"
{% block title %}User{% endblock %}
{% block page_content %}
{% if name %}
&h1& Hello {{ name }} &/h1&
&h1& Hello Stranger &/h1&
{% endif %}
{% endblock %}
5.404.html
{% extends "base.html" %}
{% block title %} 404 {% endblock %}
{% block page_content %}
&h1&404错误:页面没有找到 &/h1&
{% endblock %}
6.favicon.png
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
简单的介绍了从安装到编写一个博客。
· 90人关注
· 40人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:

我要回帖

更多关于 响应消息的内容类型 的文章

 

随机推荐