3. 视图的简单玩法
preface
我这里主要说说视图的玩法
- 响应
- 静态文件管理
- 即插视图
3.1. 标准视图
3.2. 基于调度方法的视图 - 蓝图
- 子域名
响应
视图函数的返回值会被自动转换为一个响应对象,转换的逻辑如下:
- 如果返回yied合法的响应对象,它会从视图直接返回。
- 如果返回一个字符串,会用字符串数据和默认的参数创建以字符串为主体,状态为200,MIME类型是text/html的werkzeug.wrapper.Response 响应对象。
- 如果返回的一个元组,,且元组中的元素可以提供额外的信息。这样的元组必须是(response,status,headers) 的形式,但是需要至少一个包含一个元素。status的值会覆盖状态代码,header可以是一个列表或者字典,作为额外的消息头。
- 如果上诉条件均不满足,Flask会假设返回值是一个合法的WSGI的应用程序,并通Response.force_type(rv,request.environ)转换为一个请求对象。
请看代码
@app.error_handler(404)
def not_found(error):
return render_template('templates/error.html'),404
上面的代码满足第3条,返回的元组必须是(response,status,headers) 的形式。
我们可以改为如下显式的调用make_response形式。
@app.error_handlers(404)
def not_found(error):
resp = make_response(render_template('error.html'),404)
return resp
第二种方法很灵活,可是添加一些额外的工作,比如设置cookie,头信息等。
API 都是返回JSON格式的响应,需要包装jsonify。可以抽象下,让Flask自动帮我们完成这些工作。
from flask import Flask,jsonify
from werkzeug.wrappers import Response
app = Flask(__name__)
class JSONResponse(Response):
@classmethod
def force_type(cls, rv, environ=None):
if isinstance(rv,dict):
rv = jsonify(rv)
return super(JSONResponse,cls).force_type(rv,environ)
app.response_class = JSONResponse
@app.route('/')
def hello_wolrd():
return {'msg':'hello world'}
@app.route('/custome_headers')
def headers():
return {'headers':[1,2,3]},201,[('X-Request-Id','100')]
if __name__ == '__main__':
app.run(port=80)
httpie是一个使用python编写的,提供了语法高亮,JSON支持,可以替代curl的工具,它也可以方便的集成到Python项目中。
pip install httpie # 安装
我们使用http查看下:
[root@localhost ~]# http http://0.0.0.0/custome_headers
HTTP/1.0 201 CREATED
Content-Length: 45
Content-Type: application/json
Date: Tue, 04 Apr 2017 04:01:55 GMT
Server: Werkzeug/0.12.1 Python/2.7.5
X-Request-Id: 100
{
"headers": [
1,
2,
3
]
}
静态文件管理
web应用大多数会提供静态文件服务一边给用户更好的访问体验。静态文件主要包含CSS样式文件、javascript脚本文件、图片文件和字体文件等静态资源。Flask也支持静态文件访问,默认只需要在项目根目录下创建名字为static的目录,在应用中使用“/static” 开头的路径就可以访问。但是为了获得更好的处理能力,推荐使用Nginx或者其他的web服务器管理静态文件。
不要直接在模版中写静态文件路径,应该使用url_for生成路径。举个列子:
url_for('static',filename='style.css')
生成的路径就是'/static/style.css'。当然,我们也可以定制静态文件的真实目录:
app = Flask(__name__,static_folder='/tmp')
那么访问“http://127.0.0.1/static/style.css”就是访问的是/tmp/style.css这个文件。
即插视图
即插视图灵感来自于django的基于类而不是函数的通用视图方式,这样的视图就可以支持继承了。视图类型有2种类型:
1.标准视图
标准视图需要继承flask.views.View,必须实现dispatch_request。
看一个例子:
# coding=utf-8
from flask import Flask, request, render_template
from flask.views import View
app = Flask(__name__, template_folder='templates/') # 指定模版文件
class BaseView(View):
def get_template_name(self):
raise NotImplementedError()
def dispatch_request(self):
if request.method != 'GET':
return 'UNSUPPORTED!'
context = {'users': self.get_users()}
print('context',context)
return render_template(self.get_template_name(),**context) # 必须是**content,render_template源码写的就是这样。
class UserView(BaseView):
def get_template_name(self):
return 'chapter3/section1/users.html' # html文件名字
def get_users(self):
return [{
'username': 'fake',
'avatar': 'http://lorempixel.com/100/100/nature/' # 用户头像
}]
app.add_url_rule('/users', view_func=UserView.as_view('userview')) # 添加一条URL规则,且该URL对应的视图方法
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
2.基于调度方法的视图
flask.views.MethodView 对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上),这对RESTful API尤其有用。看一个例子。
# coding=utf-8
from flask import Flask, jsonify
from flask.views import MethodView
app = Flask(__name__)
class UserAPI(MethodView):
def get(self):
return jsonify({
'username': 'fake',
'avatar': 'http://lorempixel.com/100/100/nature/'
})
def post(self):
return 'UNSUPPORTED!'
app.add_url_rule('/user', view_func=UserAPI.as_view('userview'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
解释下as_view的功能: 上面的例子是将类UserAPI使用as_view 作为视图函数 然后设置一个name 之后与/user 的 url规则绑定在一起。
即插视图继承view 实现一个自己的dispatch_request的方法。 在执行这个视图的时候会执行dispath_request这个函数里的内容,这样就可以灵活运用这个特性。
通过as_view的返回值来实现对于视图的装饰功能,常用于权限的检查,登陆验证等。
def user_required(f):
def decorator(*args,**kwargs):
if not g.user:
abort(401)
return f(*args,**kwargs)
return decorator
view = user_requird(UserAPI.as_view('users'))
app.ad_url_rule('/users/',view_func=view)
从Flask 0.8开始,还可以通过在继承MethodView类中添加 decorators 属性来实现对视图的装饰:
class UserAPI(methodView)
decorators = [user_required]
这样的话,用户在访问的时候,我们可以对用户进行验证与权限检测了。
蓝图
蓝图(blueprint)实现了应用的模块化,使用蓝图让应用层次清晰,开发者可以更容易的开发和维护项目。蓝图通常作用于相同的URL前缀,比如/user/:id、/user/profile 这样的地址,都是以/user开头的,那么他们就可以放在一个模块中。看一个最简单的例子, 我感觉这个蓝图功能就等于django的url里面的include功能一样,匹配到第一个url路径后往对应app里面走。
某个app的下面的simple.py
from flask import Blueprint
bp = Blueprint('user',__name__,url_prefix='/user')
@bp.route('/')
def index():
return "User's Index Page"
然后我们看主程序(注册到里面即可)
import blueprint
app.register_blueprint(blueprint.bp) # 注册到里面即可
子域名
现在很多SaaS应用为用户提供一个子域名来访问,可以借助subdomain来实现同样的功能。
请看例子:
# coding=utf-8
from flask import Flask, g
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com'
@app.url_value_preprocessor
def get_site(endpoint, values):
g.site = values.pop('subdomain')
@app.route('/', subdomain='<subdomain>')
def index():
return g.site
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
然后我们访问的效果如下:
[root@localhost ~]# http http://a.example.com/ --print b
a
[root@localhost ~]# http http://b.example.com:80/ --print b
b