1.4 flask URL与视图
URL与视图
URL与函数的映射
从之前的helloworld.py
文件中,我们已经看到,一个URL
要与执行函数进行映射,使用的是@app.route
装饰器。@app.route
装饰器中,可以指定URL
的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL
,文章详情的URL
是/article/id/
,id有可能为1、2、3...,那么可以通过以下方式:
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
其中<id>
,尖括号是固定写法,语法为<variable_name>
,variable_name
默认的数据类型是字符串。如果需要指定类型,则要写成<converter:variable_name>
,其中converter
就是类型名称,可以有以下几种:
-
string: 默认的数据类型,接受没有任何斜杠“\ /”的文本。
-
int: 接受整型。
-
float: 接受浮点类型。
-
path: 和string的类似,但是接受斜杠。
-
uuid: 只接受uuid字符串。
-
any:可以指定多种路径,这个通过一个例子来进行说明:
@app.route('/<any(article,blog):url_path>/') def item(url_path): return url_path
以上例子中,item这个函数可以接受两个
URL
,一个是/article/
,另一个是/blog/
。并且,一定要传url_path
参数,当然这个url_path
的名称可以随便。
如果不想定制子路径来传递参数,也可以通过传统的?=
的形式来传递参数,例如:/article?id=xxx
,这种情况下,可以通过request.args.get('id')
来获取id
的值。如果是post
方法,则可以通过request.form.get('id')
来进行获取。
构造URL(url_for)
一般我们通过一个URL
就可执行到某一个函数。如果反过来,我们知道一个函数,怎么去获得这个URL
呢?url_for函数
就可以帮我们实现这个功能。url_for()
函数接收两个及以上的参数,他接收函数名作为第一个参数,接收对应URL规则的命名参数,如果还出现其他的参数,则会添加到URL
的后面作为查询参数。
通过构建URL
的方式而选择直接在代码中拼URL
的原因有两点:
- 将来如果修改了
URL
,但没有修改该URL
对应的函数名,就不用到处去替换URL
了。 -
url_for()
函数会转义特殊字符和Unicode数据
,这些工作都不需要我们自己处理。
下面用一个例子来进行解释:
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
# 这行的代码可以在交互模式下产生请求上下文,不用`app.run()`来运行这个项目,直接可以运行下面的代码,
# 也会有`flask`上下文
with app.test_request_context():
print url_for('article',id='1')
print url_for('article',id='2',next='/')
执行后的结果如下:
> /article/1/
> /article/2/?next=%2F
自定义URL转换器
刚刚在URL映射的时候,我们看到了Flask
内置了几种数据类型的转换器,比如有int/string
等。如果Flask
内置的转换器不能满足你的需求,此时你可以自定义转换器。自定义转换器,需要满足以下几个条件:
- 转换器是一个类,且必须继承自
werkzeug.routing.BaseConverter
。 - 在转换器类中,实现
to_python(self,value)
方法,这个方法的返回值,将会传递到view
函数中作为参数。 - 在转换器类中,实现
to_url(self,values)
方法,这个方法的返回值,将会在调用url_for
函数的时候生成符合要求的URL
形式。
比如,拿一个官方的例子来说,Reddit
可以通过在URL
中用一个加号(+)隔开社区的名字,方便同时查看来自多个社区的帖子。比如访问“www.reddit.com/r/flask+lisp/”的时候,就同时可以查看flask和lisp两个社区的帖子,现在我们自定义一个转换器来实现这个功能:
#coding: utf-8
from flask import Flask,url_for
from werkzeug.routing import BaseConverter
class ListConverter(BaseConverter):
def __init__(self,url_map,separator='+'):
super(ListConverter,self).__init__(url_map)
self.separator = separator
def to_python(self, value):
return value.split(self.separator)
def to_url(self, values):
return self.separator.join(BaseConverter.to_url(self,value) for value in values)
app.url_map.converters['list'] = ListConverter
@app.route('/community1/<list:page_names>')
def community1(page_names):
return '%s+%s' % tuple(page_names)
@app.route('/community2/<list('|'):page_names>/')
def community2(page_names):
return "%s|%s" % tuple(page_names)
communityu1
使用的是默认的+
号进行连接,而第二种方式使用了|
进行连接。
from flask import Flask,url_for
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 一个url中,含有手机号码的变量,必须限定这个变量的字符串格式满足手机号码的格式
class TelephoneConveter(BaseConverter):
regex = r'1[85734]\d{9}'
# 用户在访问/posts/a+b/
class ListConverter(BaseConverter):
def to_python(self, value):
return value.split('+')
def to_url(self, value):
return "+".join(value)
# return "hello"
app.url_map.converters['tel'] = TelephoneConveter
app.url_map.converters['list'] = ListConverter
@app.route('/')
def hello_world():
print('='*30)
print(url_for('posts',boards=['a','b']))
print('='*30)
return 'Hello World!'
@app.route('/user/<string:user_id>/')
def user_profile(user_id):
return '您输入的user_id为:%s' % user_id
@app.route('/telephone/<tel:my_tel>/')
def my_tel(my_tel):
return '您的手机号码是:%s' % my_tel
@app.route('/posts/<list:boards>/')
def posts(boards):
print(boards)
return "您提交的板块是:%s" % boards
# http://127.0.0.1:8888/posts/aaa+bbb/
# 您提交的板块是:['aaa', 'bbb']
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8888,debug=True)
URL唯一
Flask
的URL
规则是基于Werkzeug
的路由模块。这个模块的思想是基于Apache
以及更早的HTTP
服务器的主张,希望保证优雅且唯一的URL
。
举个例子:
@app.route('/projects/')
def projects():
return 'project page'
上述例子中,当访问一个结尾不带斜线的URL
会被重定向到带斜线的URL
上去。这样有助于避免搜索引擎搜索同一个页面两次。
再看一个例子:
@app.route('/about')
def about():
return 'about page'
以上例子中,当访问带斜线的URL
(/about/)会产生一个404("Not Found")错误。
指定HTTP方法
GET
请求和POST
请求:
在网络请求中有许多请求方式,比如:GET、POST、DELETE、PUT请求等。那么最常用的就是GET
和POST
请求了。
-
GET
请求:只会在服务器上获取资源,不会更改服务器的状态。这种请求方式推荐使用GET
请求。 -
POST
请求:会给服务器提交一些数据或者文件。一般POST请求是会对服务器的状态产生影响,那么这种请求推荐使用POST请求。 - 关于参数传递:
-
GET
请求:把参数放到url
中,通过?xx=xxx
的形式传递的。因为会把参数放到url中,所以如果视力好,一眼就能看到你传递给服务器的参数。这样不太安全。 -
POST
请求:把参数放到Form Data
中。会把参数放到Form Data
中,避免了被偷瞄的风险,但是如果别人想要偷看你的密码,那么其实可以通过抓包的形式。因为POST请求可以提交一些数据给服务器,比如可以发送文件,那么这就增加了很大的风险。所以POST请求,对于那些有经验的黑客来讲,其实是更不安全的。
-
- 在
Flask
中,route
方法,默认将只能使用GET
的方式请求这个url,如果想要设置自己的请求方式,那么应该传递一个methods
参数。
在@app.route()
中可以传入一个关键字参数methods
来指定本方法支持的HTTP
方法,默认只响应GET
请求,看以下例子:
@app.route('/login/',methods=['GET','POST'])
def login():
return 'login'
以上装饰器将让login
的URL
既能支持GET
又能支持POST
。
页面跳转和重定向
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。
- 永久性重定向:
http
的状态码是301
,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingdong.com
的时候,会被重定向到www.jd.com
,因为jingdong.com
这个网址已经被废弃了,被改成jd.com
,所以这种情况下应该用永久重定向。 - 暂时性重定向:
http
的状态码是302
,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
在flask
中,重定向是通过flask.redirect(location,code=302)
这个函数来实现的,location
表示需要重定向到的URL
,应该配合之前讲的url_for()
函数来使用,code
表示采用哪个重定向,默认是302
也即暂时性重定向
,可以修改成301
来实现永久性重定向。
以下来看一个例子,关于在flask
中怎么使用重定向:
from flask import Flask,url_for,redirect
app = Flask(__name__)
app.debug = True
@app.route('/login/',methods=['GET','POST'])
def login():
return 'login page'
@app.route('/profile/',methods=['GET','POST'])
def profile():
name = request.args.get('name')
if not name:
# 如果没有name,说明没有登录,重定向到登录页面
return redirect(url_for('login'))
else:
return name