3. 视图的简单玩法

preface

我这里主要说说视图的玩法

  1. 响应
  2. 静态文件管理
  3. 即插视图
    3.1. 标准视图
    3.2. 基于调度方法的视图
  4. 蓝图
  5. 子域名

响应

视图函数的返回值会被自动转换为一个响应对象,转换的逻辑如下:

  • 如果返回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
posted @ 2017-04-04 13:07  温柔易淡  阅读(449)  评论(0编辑  收藏  举报