Flask的URL与视图

前言

在使用PyCharm创建一个Flask项目之后,默认会生成app.py文件,文件的默认代码如下:

from flask import Flask
import config

app = Flask(__name__)

@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

我们把@app.route中的第一个字符串参数叫做URL,把被@app.route装饰的函数叫做视图函数,可以在代码中看到URL与视图函数的映射关系如下:

@app.route('/')
def hello_world():
	return 'Hello World!'

其中,@app.route装饰器中添加了访问URL的规则"/“,”/“代表网站的根路径,只要在浏览器中输入网站的域名即可访问到”/“。被@app.route装饰的hello_world()函数会在浏览器访问”/"时被执行,此时hello_world函数没有做任何事情,只是简单的返回了Hello World字符串,因此在浏览器中访问http://127.0.0.1:5000时,我们就可以看到被打印的Hello World字符串。

3.1 定义URL

绝大部分的网站都不可能只有一个首页页面,以一个最简单的博客网站为例,博客页面相关的有博客列表、博客详情等,用户页面相关的有注册、登陆、个人中心等。所以在制作网站时,需要定义许多不同的URL来满足不同页面的访问需求,而URL总体上讲又分为两种,第一种是无参数的URL,第二种是有参数的URL,下面展开来讲。

3.1.1 定义无参数的URL

无参数URL是指在URL定义的过程中,不需要进行定义参数,这里以个人中心为例子,如果定义个人中心的URL为/profile,可以使用下面的代码来实现:

@app.route('/profile')
def profile():
    return '这是个人中心'

当我们访问:http://127.0.0.1:5000/profile时,就可以得到在该路由下的对应规则,即返回字符串:‘这是个人中心’。
请添加图片描述

3.1.2 定义有参数的URL

很多时候,在访问某个URL时需要携带一些参数,如获取博客详情时,需要把博客的id传过去,那么博客详情的URL可能为/blog/13,其中13位博客的id。假如我们现在需要获取第10页的博客列表,那么博客列表的URL可能为/blog/list/10,其中10为页码。在Flask中,如果URL携带了参数,那么视图函数也必须定义相关形参来接收URL中的参数,这里以一个实例来举例:

@app.route('/blog/<int:blog_id>')
def blog_detail(blog_id):
    return '您查找的博客id为: %s' %blog_id

URL中的参数可以指定类型,指定参数有两点好处:

  • 浏览器访问URL时,如果传的参数不能被转换为指定的参数类型,如定义URL时参数为整形,但是访问的时候传的是不能被转换为整型的参数,如hello,那么这个URL就不会被匹配,从而抛出404错误,保证网站正常运行。
  • URL本质上也是一个字符串,如果没有指定参数类型,那么参数传进视图函数时也默认是字符串类型,如果指定了参数类型, 那么在传给视图函数之前,会将参数转换为指定类型,这样视图函数拿到的参数就是经过转换后的,从而更加方便使用。

指定参数类型是通过语法:<类型:参数名>实现的。这里以/blog/<blog_id>这个URL为例,指定blog_id为int类型,就可以如上述代码所展示,就可以正常匹配路由了。

请添加图片描述
这里有很多的参数类型,举例如下:

参数类型描述
string字符串类型,可以接受除/以外的字符
int整型,可以接受能通过int()方法转换的字符
float浮点类型,可以接受能通过float()方法转换的字符
path路径,类似string,但是中间可以添加/
uuidUUID类型,由一组32位的十六进制数所构成
anyany类型,指备选值中的任何一个

这里any比较特殊,例如,现在要实现一个获取某个分类的博客列表,但是博客分类只能是Python、Flask、Django之一,那么用any就可以轻松实现。

@app.route('/blog/list/<any(python,flask,django):category>')
def blog_list_with_category(category):
    return "您获取的博客分类为:%s"%category

在访问该路由的时候,因为分类python被包含在了备选值中,所以可以正常显示内容:
请添加图片描述
但是访问其他参数,就会提示404 Not Found:
请添加图片描述
参数选择什么类型,完全取决于视图函数对这个参数的期望,如果期望是整型,那么就用int;如果期望是字符型,那么就用string。如果URL中需要传递多个参数,则只要用斜杠"/"分割即可,如果要获取某一个用户的博客列表URL,则需要传递用户id和分页页码两个参数,方法如下:

@app.route('/blog/list/<int:user_id>/<int:page>')
def blog_list(user_id,page):
    return "您获取的博客id为:%s,博客分页为:%s"%(user_id,page)

请添加图片描述
在定义URL时,总是会力求简洁, 如以上描述的获取某个用户博客列表的URL,默认情况下都在第一页,这时候如果能把page省略掉,不传这个参数,那么URL会变得更加简洁,代码可以修改如下:

# 注释掉之前的写法
# @app.route('/blog/list/<int:user_id>/<int:page>')
# def blog_list(user_id,page):
#     return "您获取的博客id为:%s,博客分页为:%s"%(user_id,page)

# 更加简介的写法
@app.route('/blog/list/<int:user_id>')
@app.route('/blog/list/<int:user_id>/<int:page>')
def blog_list(user_id,page=1):
    return "您获取的博客id为:%s,博客分页为:%s"%(user_id,page)

请添加图片描述
通过上面的代码可以看到,我们定义了两个URL,第一个URL中没有page参数,但是blog_list视图函数的page形参有一个默认值为1,这样当我们访问不带page参数的URL时,默认的page就是1,从而简化了URL的使用:
请添加图片描述
关于在URL中传递参数,还可以通过查询字符串的方式来实现,即在URL后面通过"?"把参数添加上去,如果有多个参数,则可以通过&进行拼接,规则如下:

格式:URL?参数名1=参数值1&参数名2=参数值2
举例:http://127.0.0.1/list&id=1&page=10

通过查询字符串的方式传递参数,参数先不需要在URL中定义好,只需要在访问URL时将URL参数传进来即可。下面还是以获取某个用户的博客列表为例,用查询字符串的方式传递参数,则可以通过以下URL来访问:

/blog/list?user_id=10&page=8

通过查询字符串的方式传递参数,不需要在定义URL和视图函数时提前定义好参数,参数可以通过Flask中的request.args对象获取,如以上获取某个用户博客列表的URL则可以通过以下代码来实现:

from flask import Flask,request  # 导入request
...

@app.route("/blog/list")
def blog_list_query_str():
	user_id = request.args.get("user_id")
	page = request.args.get("page")
	return "您查找的用户为:%s,博客分页为:%s" % (user_id, page)

请添加图片描述
其中request是一个线程隔离的全局对象,request.args是一个继承自dict的werkzeug.datastructures.MultiDict对象,保存了当前请求的查询字符串参数,并且被解析成以键值对的形式存在,后面就可以通过字典的方式获取参数。接下来在浏览器中访问:

http://127.0.0.1:5000/blog/list?user_id=10&page=8

请添加图片描述
通过查询字符串的方式传递参数,参数是视图函数先规定好的,然后浏览器再按照规定传递,如上面代码中视图函数是通过user_id这个键来获取用户id,如果前端传的键不是user_id,那么视图函数就无法获取到用户id,从而导致数据获取失败。

3.2 HTTP请求方式

在HTTP协议中,请求URL有不同的方式(method),不同的请求方式有不同的应用场景,这里先了解一下HTTP请求方法和使用方法:

请求方法描述
GET从服务器获取资源。在浏览器中输入网址访问默认使用的GET请求
POST提交资源到服务器。如提交表单或者上传文件,一般用于创建新资源或者修改已有的资源
HEAD类似于GET请求,响应体中不包含具体的内容,用于获取消息头
DELETE请求服务删除资源
PUT请求服务器替换或修改已有的资源
OPTIONS请求服务器返回某个资源所支持的所有HTTP请求方法, 如AJAX跨域请求常用OPTIONS方法发送嗅探请求,来判断是否有对某个资源访问的权限
PATCH与PUT方法类似,但是PATCH方法一般用于局部资源更新,PUT方法用于整个资源的替换

在Flask项目中使用app.route装饰器定义URL的时候,默认用的是GET请求方式,而在浏览器中,在地址栏中输入一个URL并进行访问,默认也是GET请求,所以可以正常访问,如果想更改URL的请求方式,可以在定义URL的时候,给app.route设置methods参数,代码如下:

@app.route("/blog/add",methods=['POST'])
def blog_add():
	return "使用POST方法添加博客"

通过上面的代码可以看到,在app.route中通过给methods参数赋值一个列表,并且列表中只有一个POST参数,来限制"/blog/add"这个URL只能通过POST方法进行访问,如下所示,会显示报错:“Method not Allowed”:
请添加图片描述
但是如果我们使用POST方式去访问,则可以正常去访问这个路由了:
在这里插入图片描述
如果需要一个URL既可以支持POST传参,又可以支持GET传参,那么可以给methods添加两个参数即可,代码如下:

@app.route('/blog/add/post/get', methods=['POST', 'GET'])
def blog_add_post_get():
    if request.method == 'GET':
        return "使用GET方法添加博客"
    else:
        return "使用POST方法添加博客"

这样就既可以通过GET请求方式访问,又可以通过POST方式访问了。
Flask从2.0版本开始,添加了5个快捷路由装饰器,如app.post表示定义的URL只接受POST请求。5个快捷路由装饰器如表:

快捷路由装饰器描述
app.get(“/login”)等价于app.route(“/login”,methods=[“GET”]
app.post(“/login”)等价于app.route(“/login”,methods=[“POST”])
app.put(“/login”)等价于app.route(“/login”,methods=[“PUT”])
app.delete(“/login”)等价于app.route(“/login”,methods=[“DELETE”])
app.patch(“/login”)等价于app.route(“/login”,methods=[“PATCH”])

3.3 页面重定向

页面重定向,指的是浏览器会从一个页面自定跳转到另外一个页面,例如。当用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此重定向到登录页面。重定向分为永久性的重定向和临时重定向,以下是具体介绍:

  • 永久性重定向:HTTP的状态码是301,多用于旧网址已被废弃,要转到一个新的网址,确保用户正常的访问,比如京东的网站,当用户输入旧域名的时候,就会跳转到新的域名,这种情况就是永久重定向。
  • 临时重定向:HTTP的状态码是302,表示页面的暂时性跳转,如访问到一个需要权限的网址或路由,但是当前用户没有登录(身份认证)或者低权限用户,这时候就会重定向到登录页面,这个是暂时性的重定向。

在Flask中,重定向是通过flask.redirect(location,code=302)函数来实现的,其中location表示需要重定向到哪个URL,code表示状态码,默认是302,即暂时性重定向。这里简单的举一个例子:

from flask import Flask,url_for,redirect # 导入库
...
app = Flask(__name__)

@app.route('/login')
def login():
	return 'login page'

@app.route('/profile')
def profile():
	name = request.args.get('name')
	if not name:
	# 如果没有name,说明没有登录,重定向到登录页面
		return redirect("/login")
	else:
		return name

从上面的代码就可以看出,在访问/profile的时候,如果没有通过查询字符串的方式传递name,那么就会被重定向到/login,如果访问/profile?name=admin就可以看到在浏览器中显示admin,但是直接访问就会被弹回到/login。

3.4 构造URL

在前面执行redirect(“/login”)函数,让页面跳转到登录页面,这里是直接把/login这个路由硬编码进去的,对于项目的健壮性不太好,更好的方式应该是通过url_for函数来动态地构造URL。url_for接受视图函数名作为第1个参数,以及其他URL定义时的参数,如果还添加了其他参数,则会添加到URL的后面作为查询字符串参数。这里简单的举一个例子:

@app.route('/blog/<int:blog_id>')
def blog_detail(blog_id):
    return "您获取的博客id为:%s" % blog_id


@app.route('/urlfor')
def get_url_for():
    url = url_for("blog_detail", blog_id=2, user="admin")
    return url

在get_url_for视图函数中使用了url_for函数,把函数名blog_detail作为第1个参数,因为blog_detail的URL需要接受一个blog_id参数,因此把blog_id也传递给了url_for函数,除此之外,还添加了一个user参数,因为user参数不是必需的,所以在构建URL后,会把user作为查询字符串参数拼接上去,这样访问/urlfor路由,就可以看到:
请添加图片描述
相比在代码中硬编码URL,使用url_for函数动态的构建URL函数有以下两个好处:

  • URL是对外的,可能会经常发生变化, 但是视图函数不会经常发生变化。如果直接把URL硬编码,若后期URL变化了,凡是硬编码的URL都需要修改。
  • URL在网络通信的过程中,需要把一些特殊字符包括中文在内的进行编码,如果URL中包含了特殊字符,用url_for函数会自动进行编码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如此李想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值