一、Flask框架介绍
使用python开发后端,目前主流的框架就是Flask和Django。其中,Flask是一款轻量级的Python web框架,有以下主要特点:
1. 简单易用
Flask很简单易用,可以快速上手开发web应用。它只依赖Werkzeug和Jinja2两个库,没有象Django那样的重量级复杂的功能。
2. 灵活
Flask非常灵活,可以适用于不同的项目规模。从简单的"Hello World"到大型项目都可以使用Flask开发。并且它可以与更多的flask扩展结合,实现更丰富的功能。相对来讲,Django只适合较大的工程。
3. Pythonic
Flask使用Python语言开发,遵循Pythonic的设计理念,语法优雅简洁,具有很强的可读性。这使得Flask非常适合学习和理解。
4. RESTful
Flask天生支持RESTful风格的web服务开发。HTTP一级支持,简单的URL规则等使其非常适合构建REST API。
5. 扩展性强
Flask最重要的一个特点就是拥有丰富的扩展,几乎涵盖了web开发所有的功能,如:ORM(Flask-SQLAlchemy)、表单验证(Flask-WTF)、文件上传(Flask-Uploads)、用户认证(Flask-Login)等。这些扩展可以根据需要很容易集成到Flask应用中。需要什么插件,就安装什么插件。
6. 部署方便
Flask的应用十分简单,甚至一个py文件就行。它支持与各种Web服务器兼容,如WSGI服务器、Nginx和uWSGI等。
7. 社区优秀
Flask拥有庞大的社区,官方文档资料也比较齐全。几乎所有的问题都可以在网上找到答案。
所以,总的来说,Flask是一个简单、灵活、易于理解、可扩展性强的Python web框架。它适用于学习、 小型项目以及复杂项目。具有非常优秀的社区资源和生态环境。
二、RESTFul协议特点
1. 基于HTTP
RESTful API基于HTTP协议,使用HTTP方法为资源定义操作。如GET用于读取,POST用于创建,PUT用于更新,DELETE用于删除等。有的API接口,无论是读取还是更新,都统一使用POST方法,而把实际的动词包含在URL里,如'/data/delete'。
2. 无状态
RESTful API是无状态的,它不存储任何客户端上下文信息。每个请求必须包含请求所需要的所有信息,服务器根据请求进行响应。
3. 层次结构
RESTful API使用统一的接口访问命名空间中的所有资源。这些资源可以被层次化,形成资源的树形结构。
4. 使用URL定位资源
每个资源都有一个唯一的URL来指定和定位,这一点和文件夹路径原理是一样的。
5. 可缓存
RESTful API响应可以是可缓存的,这可以提高效率和性能。缓存可以存在于服务器端、客户端或代理服务器。这一点对于频繁请求某一资源时很有帮助。
6. 统一接口
RESTful API将所有资源通过统一的接口来访问和操作。这简化了整个系统。
7. 跨语言兼容
RESTful API返回的通常为JSON或XML格式。这使其对各种语言和平台更易于实现和兼容。
REST是一种优秀的开发思路和规范,但我们在实际开发时也要辩证来看。规范和通用的东西必然在灵活性上有所不足。如果我们开发的是对外的、通用的接口,那么REST是一个好的选择。但如果这个接口只对内部提供,则不必严格按照REST协议来。因为实际的业务往往是复杂的、具体的和相对固定的。那么针对性地提供一些接口,会让前端开发(调用者)更容易。某种程度,也可以减少一定的http请求次数。如某个页面有几十个数据需要请求,按照REST风格来可能需要请求几十个接口,而我们可以设计一个统一接口将全部信息返回(有点夸张了)。
三、路由
1、路由的概念
百度百科的解释是:“路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程”。在TCP/IP协议里,路由器的工作就是将不同的信息根据ip地址进行分发。而在web开发中,概念也基本一致。即将前面提到的url与资源进行一种映射,请求相应的url则获取对应资源。而建立这种映射的过程就叫注册路由。
2、两种路由注册方式
from flask import Flask
app = Flask(__name__)
# 方法一
@app.route('/hello')
def hello():
return 'hello world'
if __name__ == '__main__':
app.run('0.0.0.0', 5555)
第一种方法是利用了python装饰器的特性,route方法内部仍然是调用add_url_rule。本质是一样的,只是装饰器看起来更优雅。
关于装饰器的解释可以参考下文Python 装饰器_function databaseerrorwrapper.__call__.<locals>.in_Jiangugu的博客-CSDN博客
from flask import Flask
app = Flask(__name__)
def hello():
return 'hello world'
# 方法二
app.add_url_rule('/hello', view_func=hello)
if __name__ == '__main__':
app.run('0.0.0.0', 5555)
四、Request请求对象
以百度搜索关键词python为例,可以通过后台查看请求对象包含的信息
可以看到,虽然我们只输入了一个python,但实际的请求包含了很丰富的信息。如请求头里包含的请求地址、方法 ,请求体里包含的关键字等。在一些需要登陆的网站,往往还需要携带cookies。本文主要还是讲解利用flask搭建后端,主要作为响应端。即如何响应请求,关于更多request的介绍请参考下文。
Python 网络开发基础之Requests_request是技术还是库_Jiangugu的博客-CSDN博客
五、Response响应对象
响应对象和请求对象一样,除了“内容”部分,还会包含诸多其他信息。常见的如content-type、status_code。在flask里,必须指定的是内容部分,其余均有默认值。
1、content-type
默认的content-type是html,那么返回的字符串就会按照html的格式去解析,因此前端显示为空。
@app.route('/test')
def test():
return '<html></html>'
如果想显示这段文本,则手动指定content-type即可
@app.route('/test')
def test():
header = {
'content-type': 'text/plain'
}
return '<html></html>', header
返回一个网页的示例
@web.route('/square/<p>', methods=['GET'])
def square(p):
"""
:param p:
:return: 返回的是一个html
"""
return render_template('test.html', result=int(p)**2)
这是前端部分(样式CSS和脚本JS没有展开,本文主要讲解后端和接口)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>'测试网页'</title>
</head>
<body>
<div>
<p style="color: red">计算结果是:{{ result }}</p>
</div>
</body>
</html>
而对于API来说,content-type为'application/json',即要求调用者按照json格式去解析。
2、状态码
常见状态码包括200(请求成功)、301(重定向)、404(未找到资源)、500(服务器内部错误)。这里需要强调的是,状态码只是一种约定。你完全可以约定请求成功是404,只要事先和请求方约定好。
虽然浏览器报错,但内容仍正常显示。此外,在接口开发中,可能出现的情况会很多。仅有的几个状态码一般都是不够用的。往往会在内容content里再约定一个如status这样的变量,约定1是成功,2是没有权限,3是参数不够,4是数据范围超出……
from flask import Flask
import json
app = Flask(__name__)
@app.route('/test/<nums>')
def test(nums):
dt = nums.split(',')
header = {
'content-type': 'application/json',
}
if len(dt) > 10:
return json.dumps({'status': 4, 'msg': '数据长度不能超过10'}), 500, header
else:
return json.dumps({'status': 1, 'result': 111}), 200, header
if __name__ == '__main__':
app.run('0.0.0.0', port=8000, debug=True)
六、表单
除了URL里可以携带参数,还有一种常见的传参方式就是通过表单,如登录和注册页面填写的账号、密码等信息。后端如何接收这些信息呢?
<html>
<head>
<title>测试网页</title>
<link rel="stylesheet" type="text/css" href="static/login.css"/>
<link rel="shortcut icon"
href="{{ url_for('static', filename='pics/logo.png') }}">
</head>
<body>
<div id="main">
<div id="top">
<img src={{url_for('static', filename='pics/pure_logo.png')}} id="logo">
</div>
<div id="bottom">
<form method="POST">
<p class="titleofinput">用户名: </p>
<input type="text" name="username" class="input" autocomplete="off" placeholder="请输入用户名" required>
<p class="titleofinput">密码: </p>
<input type="password" name="password" class="input" autocomplete="off" placeholder="请输入密码" required>
<br>
<button type="submit" id="submit">登录</button>
</form>
</div>
</div>
</body>
</html>
这个简单的登录界面示例代码中,输入用户名和密码使用的是<input>标签且存在于<form>里。而它们都有一个name属性,因此后端可以通过这个属性获得对应传递过来的值。
from flask import request, url_for, redirect, render_template
from flask_login import login_user
from model.user import User
from . import web
@web.route('/login', methods=['GET', 'POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
if request.method == 'POST':
# 自定义的user模型,将具体的校验方法封装其中,也可以直接粗暴地写在这里
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
# 将用户登录票据写入客户端cookies, remember开启默认保存365天
login_user(user, remember=True)
return redirect(url_for('web.do'))
else:
return '用户名或密码错误'
return render_template('login.html')
七、其他零散知识点
1、IP地址
我们的app在绑定地址时,可以写网卡的实际地址,如192.168.0.111(固定地址,地址改变时需同步更改),也可以写127.0.0.1(只允许本机访问),还可以写0.0.0.0。一般我们在开发的时候,随便写哪个都可以,因为都是本机访问且地址不会变。但如果地址可能会变,同时正式部署后要允许其他计算机访问,就只能写0.0.0.0。
2、端口
原则上,端口可以在1~65535范围内任意指定,只要不与已有的端口冲突即可。如22是ssh通讯端口,3306是数据库端口。而web端口默认是80,即使用默认端口时,浏览器访问可以不指定端口。
3、调试模式
后端开发是一个交互性很强的工作,需要一边调整一边看结果。因此,如果每次修改代码都需要重启应用的话会很浪费时间。在flask里只需要打开调试模式即可实现类似监听的功能,代码的修改能立刻应用。
app.run('0.0.0.0', 8000, debug=True)
4、参数配置
一个优秀的web服务,离不开诸多参数配置。如前面提到的端口号、调试模式等。实际上很多代码工程都需要配置参数,而一个好的参数配置需要具备“可读性强”,“易修改”等特点。因此,最好的方式就是在一个统一的地方,统一对参数进行定义和赋值,在整个工程其他地方使用定义的变量名。这里有两种方式推荐,其一是配置系统环境变量,然后在代码里通过os.environ.get('变量名')。第二种方法是创建一个配置文件,如config.py,在这个文件里配置一些参数,如DEBUG=True,注意变量名需全部大写。
# 加载配置模块
app.config.from_object('config')
# 调用相应配置参数
app.run('0.0.0.0', debug=app.config['DEBUG'])
5、重定向
有一些资源暂时没有或不想让别人访问,除了抛出404,还可以重定向。即建议请求者重新请求一个地址。
@app.route('/test')
def test():
header = {
'content-type': 'text/plain',
'location': 'https://www.baidu.com'
}
return '<html></html>', 301, header
6、传参
(1)多参数传参
我们在上面的案例中,参数是直接跟在url后的。那是因为参数只有一个,不太容易引起误解。如果查询参数较多,一般可以使用以下方式
from flask import request
import json
@app.route('/plus')
def plus():
a = request.args['a']
b = request.args['b']
header = {
'content-type': 'application/json',
}
return json.dumps({'status': 1, 'result': int(a) + int(b)}), 200, header
(2)数组传参
在调用API接口时,有时会需要传递一个较大的数据。这时候就不适合将数据作为查询参数添加到url里。一方面会导致url可读性较差,同时,url也会有长度限制(因浏览器,服务器等不同,一般限制几千个字符)。此时,可以将数据放到请求体里。
from flask import Flask, request
import json
app = Flask(__name__)
@app.route('/test/')
def test():
dt = request.json['data']
header = {
'content-type': 'application/json',
}
if len(dt) > 10:
return json.dumps({'status': 4, 'msg': '数据长度不能超过10'}), 400, header
else:
return json.dumps({'status': 1, 'result': sum(dt)}), 200, header
if __name__ == '__main__':
app.run('0.0.0.0', port=5555, debug=True)
7、Cookie
由于http请求是没有记忆的,每次请求都是独立没有上下文关联的。但有时我们需要这种上下文关联,比如记录登录信息、购物网站的购物车、搜索记录等。记录这种信息的载体就是Cookie,它是由服务器生成并写入访问客户端的。客户端在下一次的访问中就会携带该信息,以便继续对话。Cookie是以键值对的形式存储的,除了key-value的信息,一般还包括允许访问的域名、有效期等信息。使用flask框架的服务可以通过以下方式生成Cookie
from flask import make_response
@app.route('/')
def index():
# 这里快速构造了一个response对象,和前面的一样
resp = make_response('Hello')
# 有效期1000秒
resp.set_cookie('username', 'john', 1000)
return resp
访问相应的视图函数,此时浏览器已经被写入Cookie。