本教程适合于掌握了Python的基础语法,掌握了基本的MySQL语法,了解HTML,CSS,JS等前端技术,了解基本的Web技术原理,了解HTTP协议更好。
有过Python基础的同学应该都知道,在我们之前写Python代码后,想要查看运行结果一般都是在IDE工具pycharm等的黑窗口来显示我们代码的运行结果的,而现在我们知道无论是网站,还是app,再或者微信相关应用都是展示在一个好看的网页上面的,那我们怎么将我们的运行结果展示到网页上呢?这个时候就需要用到我们的web服务器了。
结合我们日常生活中碰到的实例,比如我们要发送邮件,当然我们可以用QQ或者163邮箱去给别人发送邮件,作为普通用户我们所做的就只是填上对方的邮箱地址,写上我们要发送的内容,点击发送即可。可大家了解这封邮件发送背后的事情吗?很明显,背后是有一个邮件服务器为我们提供发送邮件服务的。这个邮件服务器就是安装到物理服务器里面的一个类似软件的邮件服务器程序,这个邮件服务器就像我们装在电脑里面的QQ,微信等。只是邮件服务器做的事情是要帮助别人在互联网上发送邮件,而QQ和微信是提供给个人聊天用的工具。但是他们本质都是用代码写的软件程序。
那么回到我们的web应用上,我们要做一个网站,实际上,最重要的就是要提供一个可供浏览器通过URL访问的web服务,而我们的web框架比如flask、django都内置了web服务,首先我们要先运行这个web服务,运行以后,这个服务就会给我们提供一个ip地址,比如:127.0.0.1:5000 (ip地址:端口号)。我们就可以通过在这个地址后面加上不同的地址来访问到不同的函数了。
早前提到web开发,一般指网站开发,当然现在web开发包括的范围更广了,包括电脑端网站,移动端(手机、平板等)网站,App,微信内嵌页面,微信小程序。
原理:以电脑端网站举例,在浏览器地址栏输入URL地址,向web服务器发送请求,服务器根据URL地址找到相应资源,返回所请求的资源,浏览器接收到返回的结果(HTML,CSS,JS,图片),将这些资源进行解析并组装,就形成了我们看到的各种绚丽多彩,功能齐全的网站了。
那么web服务器是怎么解析由浏览器端发起的一次HTTP请求呢?
为了搞明白这个过程,让我们首先来看一下什么是url
URL是Uniform Resource Locator的简写,统一资源定位符。
一个URL由以下几部分组成:
scheme://host:port/path/?query-string=xxx&aaa=ccc
scheme:代表的是访问的协议,一般为http或者https以及ftp等。
host:主机名,域名,比如www.baidu.com。
port:端口号。当你访问一个网站的时候,浏览器默认使用80端口。
path:查找路径。比如:www.jianshu.com/trending/now,后面的trending/now就是path。
query-string:查询字符串,比如:www.baidu.com/s?wd=python,后面的wd=python就是查询字符串。
下面我们再看看,我们如果通过上面的地址访问到某个网站的服务器后,会发生什么。
用图示的方式来给大家展示一下请求的过程:
web服务器(Nginx)和应用服务器(uWSGI)以及web应用框架(flask):
web服务器:负责处理http请求,响应静态文件如HTML,css,js,图片等这些都可以通过web服务器直接返回,常见的有Apache,Nginx以及微软的IIS.
uWSGI应用服务器:对于需要业务逻辑的请求,是不能直接通过nginx这种web服务器来处理的,只能通过应用服务器来处理,常见的应用服务器有uWSGI、tomcat等。Nginx通过自身的HttpUwsgiMouduel模块将请求转发给uWSGI,uWSGI再通过WSGI协议将请求转发给flask框架进行业务逻辑的处理。
web应用框架(flask):一般使用某种语言,封装了常用的web功能的框架就是web应用框架,flask、Django以及Java中的SSH框架都是web应用框架。
flask是一款非常流行的Python web框架,Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
flask框架自发布以来,深得开发者喜爱,flask能如此流行的原因在于:
①、微框架、简洁、只做他需要做的,给开发者提供了很大的扩展性。
②、开发效率非常高。
③、设计非常优秀。
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(操作数据库的扩展Flask-SQLAlchemy),都需要用第三方的扩展来实现。
两大核心模块 Werkzeug(路由模块)和 Jinja2(模板引擎)。为flask解决了路由和模板两个web开发的必要功能。更多功能的实现就需要安装各种扩展包了。
pip install flask
# 从flask这个框架中导入Flask这个类
from flask import Flask
# 初始化一个Flask对象
# Flaks()
# 需要传递一个参数__name__
# 方便flask框架去寻找静态资源css、js、图片等
app = Flask(__name__)
# @app.route是一个装饰器
# @开头,并且在函数的上面,说明是装饰器
# 这个装饰器的作用,是做一个url与视图函数的映射
# 127.0.0.1:5000/ -> 去请求hello_world这个函数,然后将结果返回给浏览器
@app.route('/')
def hello_world():
return 'Index'
# 如果当前这个文件是作为入口程序运行,那么就执行app.run()
if __name__ == '__main__':
# app.run()
# 启动一个应用服务器,来接受用户的请求
# while True: 相当于一直做while循环 ,监听某个端口的请求,从而发出响应
# listen()
app.run()
以上代码就是flask的最小实例,在IDE工具pycharm中,将上面代码保存到名为start.py的文件当中,然后在pycharm中在该文件上击右键运行该文件,或者在命令行输入python start.py来运行该文件。如果你的pycharm配置好了Python的开发环境,正常就会提示你此应用已经正常启动,也就是我们的web服务器启动成功,可以看到给出的地址:http://127.0.0.1:5000/ 。把此地址输入到url中进行访问,就可以看到浏览器显示出来:Hello World。就此我们的flask的第一个最小实例,书写,运行,测试成功!
刚才发生了什么?
让我来一行行解释下上面的代码,
from flask import Flask # 导入flask核心包
我们现在要做的web开发就是基于flask框架的web开发,所以,自然我们第一步就是要导入我们flask框架(库)的核心包,我们下面做的所有的操作都是基于这个包的各种方法。
app = Flask(__name__) # 实例化Flask类为app对象,传递当前文件所在位置
引入核心包之后,自然就要进行实例化,然后赋值给一个变量app,这样我们就可以通过app这个变量,来调用各种方法,实现我们下面要做的功能。另外传递进去的参数__name__相当于定义了我们web服务的根目录。(这一点的作用现在还体现不出来,当以后引入静态文件的时候就能看出它的作用了。)
@app.route('/') # 通过装饰器形式设定路由规则,同时绑定到下面紧跟着的函数上
def index(): # 由于此函数设置了路由规则之后,就可以供浏览器进行访问了,所有叫做视图函数
return 'Index' # 此函数的返回值即展示到浏览器上的信息
定义路由和视图函数的对应关系。什么叫做路由:就像我们的路由器一样,将我们不同的url一一对应到我们不同的函数上。@app.route(’/’)通过装饰器的形式,使用flask的路由模块定义路由规则,此路由规则所对应的函数就是下面紧跟着的这个函数,在flask框架中,一般将这种可以通过地址访问展示到浏览器的函数叫做视图函数,而且展示的是此视图函数的返回值,如果改成print,则只会打印到控制台,而不会展示到浏览器中。
if __name__ == '__main__': # 为了避免由别的文件通过import引入当前文件后造成的重复启动web服务器的问题
app.run()
这段代码就是启动了flask框架内置的一个简易的web服务器,当然这种服务器只适合在开发时候测试用,真正上线的时候还是要使用Gunicorn等web服务器。
由上面我们知道,我们启动服务器之后,在控制台就会提示我们访问http://127.0.0.1:5000 ,这里的5000就是flask框架服务器的运行端口。那么我们是否i可以自定义启动端口呢?答案是肯定的,我们可以通过传递参数的形式来改变服务器的启动端口。
if __name__ == '__main__':
app.run(port=8000) # 设置服务器端口
此时我们如果修改index视图函数当中的返回值为index666
,我们现在刷新页面,是不能看到修改的结果的,我们必须重启下服务器才能看到修改后的返回值。那么有什么好的办法能让我们每次修改之后不用手动重启服务器呢?可以再设置一下服务器的启动模式为调试模式。
if __name__ == '__main__':
app.run(port=8000, debug=True) # 设置开启debug模式,使得每次修改代码都能重启服务器,从而可以立即查看修改后的结果。
我们在真正开发的时候,一般会把各种配置项放到一个单独的文件当中,然后在需要使用配置项的地方引入该文件。下面我们新建一个文件setting.py
,在此文件当中写入如下代码。
DEBUG = True
将启动服务器的代码去掉debug参数的配置。修改为如下:
if __name__ == '__main__':
app.run(port=8000)
现在我们再次重启的话,我们能看到调试模式已经变成off了。我们怎么来使用配置文件呢?需要在app下面引入配置文件。
app = Flask(__name__)
app.config.from_object('setting') # 引入.py的配置文件
再次启动的话,就可以看到配置文件已经成功生效了。
我们还有另外的一种配置文件形式,就是新建一个setting.ini的文件,里面同样写入跟setting.py一样的配置。同理也是需要引入的,可以注释掉刚才的引入配置项的代码,来引入新的setting.ini文件,我们需要这样引入。
app = Flask(__name__)
# app.config.from_object('setting') # 引入.py的配置文件
app.config.from_pyfile('setting.ini') # 引入.ini的配置文件,主要需要带上后缀名
再次重启,同样达到了效果。
我们可以看到,当前文章的地址为:https://blog.csdn.net/lingjiphp/article/details/89477266 根据这个地址我们能推测到什么呢?我们是不是能够看出对于此网站的文章详情页面,前面的地址都是一样的,不一样的就是最后一个斜杠后面的数字呢?我们可以通过传递不同的数字来显示不同的文章详情页面。那么,我们是不是也可以通过flask框架实现这种效果呢?答案是肯定的,我们可以用路由传参的方式来实现这一效果。我们可以定义一个这样的视图函数:
@app.route('/detail/' )
def detail(id):
return '文章ID为{}的内容'.format(id)
flask框架就是利用这种尖括号的形式来传递地址栏输入的数据,然后通过函数的参数的形式,再把参数传递到视图函数内部供内部使用。注意这里的id必须要从外到内对应进来。然后我们就可以通过访问地址:http://127.0.0.1:8000/detail/1 就可以看到显示为:文章ID为1的内容
。这里涉及到format函数的使用,{}相当于设置了一个占位符,然后后面紧跟着.format,把需要替换占位符中内容的变量传递给format函数。那么变量id就可以替换掉{}占位符的内容了。
另外,我们还可以规定传递过来的参数的类型。比如定义下面的视图函数:
@app.route('/user_info/' )
def user_info(user_id):
return '用户ID为{}'.format(user_id)
对于这个视图函数,我们输入数字的话,肯定可以正常访问,如果我们输入字符串,就会提示我们找不到地址,这就表示,此地址只支持后面跟着整型,不支持字符串。
下面定义一个支持字符串的视图函数:
@app.route('/user/' )
def user(name):
return '用户名为{}'.format(name)
当然传递字符串肯定没有问题,那么我们如果传递数字呢?我们发现也是可以正常访问的?那么为什么呢?很明显,把数字当成字符串来对待了,所以可以正常访问,但是对于上面传递字符串的话是无论如何也无法当成整型来对待的吧?
以下两个函数都需要提前引入一下
from flask import Flask, url_for, redirect
flask框架帮我们提供了在视图函数内部可以获取别的视图函数地址的方法url_for。比如我们定义如下视图函数:
@app.route('/demo')
def demo():
print(url_for('hello')) # url_for当中传递的是视图函数名称,而不是路由规则,且要放在引号中。
return 'demo'
如果访问:http://127.0.0.1:8000/demo 就会在控制台看到打印/hello
字样。这就是打印出了hello视图函数的地址。在我们正在使用的时候,会自动加上前面的网站跟路径。最终得到这样的地址:http://127.0.0.1:8000/hello 很明显这就是我们访问视图函数hello的地址。
结合重定向函数redirect,我们就可以在我们访问一个视图函数的时候跳转到另一个视图函数。
@app.route('/demo_hello')
def demo_hello():
return redirect(url_for('hello'))
我们现在访问:http://127.0.0.1:8000/demo_hello 就会立即跳转到:http://127.0.0.1:8000/hello
url_for还支持传递第二个参数
@app.route('/demo_detail')
def demo_detail():
return redirect(url_for('detail', id=3))
这样,在我们访问:http://127.0.0.1:8000/demo_detail 就会立即跳转到:http://127.0.0.1:8000/detail/3 注意这里的id是必须和detail的id对应起来的。