绝大部分网站都不可能只有首页一个页面,以一个最简单的博客网站为例,博客页面相关的有博客列表、博客详情等,用户页面相关的有注册、登录、个人中心等。所以在制作网站时,需要定义许多不同的URL来满足不同页面的访问需求,而URL总体上来讲又分为两种,第一种是无参数的URL,第二种是有参数的URL,下面分别进行讲解这两种URL的定义。
无参数URL是指在URL定义的过程中,不需要定义参数。这里以个人中心为例,如定义个人中心的URL为/profile,可以使用以下代码实现。
@app.route('/profile') def profile(): return '这是个人中心'
这样在浏览器中访问http://127.0.0.1:5000/profile时,就可以看到“这是个人中心”的页面,如图3-3所示。
图3-3 在浏览器中访问http://127.0.0.1:5000/profile
我们说的访问/profile是不包含域名和端口号的,真正在浏览器中访问时应该在前面加上域名和端口号,如在本地开发应该为http://127.0.0.1:5000/profile,下文说的URL都是省略了域名和端口号的。
很多时候,在访问某个URL时需要携带一些参数。如获取博客详情时,需要把博客的id传过去,那么博客详情的URL可能为/blog/13,其中13为博客的id。假如获取第10页的博客列表,那么博客列表的URL可能为/blog/list/10,其中10为页码。
在Flask中,如果URL中携带了参数,那么视图函数也必须定义相应的形参来接收URL中的参数。这里以博客详情的URL为例,示例代码如下。
@app.route("/blog/<blog_id>") def blog_detail(blog_id): return "您查找的博客id为:"%blog_id
通过以上代码可以看到,URL中多了一对尖括号,并且尖括号中多了一个blog_id,这个blog_id就是参数。然后在视图函数blog_detail中,也相应定义了一个blog_id的形参,当浏览器访问这个URL时,Flask接收到请求后,会自动解析URL中的参数blog_id,把它传给视图函数blog_detail,在视图函数中,开发者就可以根据blog_id从数据库中查找到具体的博客数据返回给浏览器。关于数据库操作,这里不做过多讲解,后续章节会详细讲到。现在在浏览器中输入http://127.0.0.1:5000/blog/1,可以看到如图3-4所示效果。
图3-4 在浏览器中访问http://127.0.0.1:5000/blog/1的效果
URL中的参数还可以指定其类型,指定参数类型有以下两点好处。
浏览器访问URL时,如果传的参数不能被转换为指定的参数类型,如定义URL时参数为整型,但是访问的时候传的是不能被转换为整型的参数,如hello,那么这个URL就不会被匹配,从而抛出404错误,保证网站的正常运行。
URL本质上是一个字符串,如果没有指定参数类型,那么参数传进视图函数时默认也是字符串类型。如果指定了参数类型,那么在传给视图函数之前,会将参数转换为指定类型,这样视图函数拿到的参数就是经过转换后的,从而更加方便使用。
指定参数类型是通过语法:<类型:参数名>实现的。这里以/blog/<blog_id>这个URL为例,假如需要指定blog_id为int类型,那么代码将修改成如下形式。
@app.route("/blog/<int:blog_id>") def blog_detail(blog_id): return "您查找的博客id为:%s"%blog_id
如果在浏览器中访问/blog/hello,将显示Not Found,因为hello不能被转换为int类型,也就不会被匹配到这个URL了,所以会提示URL没有找到,如图3-5所示。
图3-5 访问/blog/hello失败
除了int类型以外,URL中的参数还可以指定其他类型,如表3-1所示。
表3-1 URL中的参数类型
表3-1中的参数类型除了any以外,其他的都好理解。这里着重讲解参数类型any的使用。例如,现在要实现一个获取某个分类的博客列表,但是博客分类只能是python、flask、django之一,用any就可以轻松实现。
@app.route("/blog/list/<any(python,flask,django):category>") def blog_list_with_category(category): return "您获取的博客分类为:%s"%category
在浏览器中访问/blog/list/python,因为博客分类python被包含在了备选值中,所以可以正常显示内容,如图3-6所示。
图3-6 访问/blog/list/python
但是访问/blog/list/java,将会显示Not Found,如图3-7所示。
图3-7 访问/blog/list/java
参数选择什么类型,完全取决于视图函数对这个参数的期望,如果期望是整型,那就用int;如果期望是字符串类型,那就用string,其他亦然。
如果URL中需要传递多个参数,则只要用斜杠(/)分隔开来即可。如要获取一个某个用户的博客列表的URL,则需要传递用户id和分页页码两个参数,相关代码如下。
@app.route("/blog/list/<int:user_id>/<int:page>") def blog_list(user_id,page): return "您查找的用户为:%s,博客分页为:%s"%(user_id,page)
所以当需要获取用户id为10、页码为8的博客列表数据时,可以通过访问/blog/list/10/8来实现,如图3-8所示。
图3-8 访问/blog/list/10/8
在定义URL时,总是会力求简洁,如以上描述的获取某个用户博客列表的URL,默认情况下都是在第1页,这时如果能把page省略掉,不传这个参数,那么URL会变得更加简洁。代码可以修改为如下形式。
@app.route("/blog/list/<int:user_id>") @app.route("/blog/list/<int:user_id>/<int:page>") def blog_list(user_id,page=1): return "您查找的用户为:%s,博客分页为:%s"%(user_id,page)
通过以上代码可以看到,我们定义了两个URL,第一个URL中没有page参数,但是blog_list视图函数的page形参有一个默认值为1,这样当访问不带page参数的URL时,默认的page就是1,从而简化了URL的使用,如图3-9所示。
图3-9 访问/blog/list/10
关于在URL中传递参数,还可以通过查询字符串的方式来实现,即在URL后面通过?(英文问号)把参数添加上去,如果有多个参数,则通过&进行拼接,规则如下。
URL?参数名1=参数值1&参数名2=参数值2
通过查询字符串的方式传递参数,参数先不需要在URL中定义好,只需要在访问URL时将参数传进来即可。下面还是以获取某个用户的博客列表为例,用查询字符串的方式传递参数,则可以通过以下URL来访问。
/blog/list?user_id=10&page=8
通过查询字符串的方式传递参数,不需要在定义URL和视图函数时提前定义好参数,参数可以通过Flask中的request.args对象获取,如以上获取某个用户博客列表的URL,可以通过以下代码来实现。
from flask import Flask,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对象,保存了当前请求的查询字符串参数,并且被解析成以键-值对的形式存在,后面就可以通过字典的方式获取参数。接下来在浏览器中访问/blog/list?user_id=10&page=8,效果如图3-10所示。
图3-10 访问/blog/list?user_id=10&page=8
通过查询字符串的方式传递参数,参数是视图函数先规定好的,然后浏览器再按照规定传递。如以上代码中的视图函数是通过user_id这个键来获取用户的id,如果前端传的键不是user_id,那么视图函数将获取不到用户id,从而导致数据获取失败。关于数据获取失败后如何处理,后续章节会详细讲解。
在URL中定义参数和查询字符串虽然都能传递参数,但还是有些区别。在URL中定义参数是将参数嵌入URL,实际上已经成为URL的一部分;而查询字符串是HTTP协议层面用于传递参数的技术,后面学到获取POST请求参数时,读者会有更深的理解。这两种方式各有利弊,下面做了简单的总结,读者在开发网站时可自行衡量。
在URL中定义参数再传递参数比通过查询字符串的方式传递参数会更利于SEO优化,能更好地被搜索引擎收录和检索。如CSDN博客网站(blog.csdn.net)的博客详情页面的博客id就是通过在URL中定义参数再传递参数的。简书网站(www.jianshu.com)的文章详情,也同样用的是在URL中定义参数的方式传递文章id的。这两个网站的SEO优化做的都是非常不错的。
在URL中定义参数可以做好类型约束,不会让错误类型的数据匹配进URL,从而提高了程序的健壮性。
通过查询字符串的方式传递参数更加灵活,不需要修改URL,也方便随时添加或者修改参数。