1.Anaconda虚拟环境创建 - Flask安装-路由-请求响应
Flask 基础
简介:
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展,其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
官网: https://flask.palletsprojects.com/en/1.1.x/
官方文档: [http://docs.jinkan.org/docs/flask/](
Flask常用第三方扩展包:
- Flask-SQLalchemy:操作数据库,ORM;
- Flask-script:终端脚本工具,脚手架;
- Flask-migrate:管理迁移数据库;
- Flask-Session:Session存储方式指定;
- Flask-WTF:表单;
- Flask-Mail:邮件;
- Flask-Bable:提供国际化和本地化支持,翻译;
- Flask-Login:认证用户状态;
- Flask-OpenID:认证, OAuth;
- Flask-RESTful:开发REST API的工具;
- Flask JSON-RPC: 开发rpc远程服务[过程]调用
- Flask-Bootstrap:集成前端Twitter Bootstrap框架
- Flask-Moment:本地化日期和时间
- Flask-Admin:简单而可扩展的管理接口的框架
可以通过 https://pypi.org/search/?c=Framework+%3A%3A+Flask 查看更多flask官方推荐的扩展
1.创建flask项目
创建虚拟环境
Anaconda3.x 安装
官方地址:https://www.anaconda.com/products/individual
页面最下方找到Linux版本,选择第一个下载。下载完成以后打开终端,执行命令:
bash Anaconda3-2020.11-Linux-x86_64.sh
一路回车,再输入yes
这里是询问anaconda的安装保存位置,默认是当前用户家目录下。将来anaconda
的所有虚拟环境都在这个目录下的envs目录下。
安装完成了。接下来,验证下是否安装成功了。关闭当前终端,打开一个新的
终端,命名行左边出现 (base) ,而且输入python3回车出现Anaconda则表示安装
并可以正常使用了。base是Anaconda自带的全局环境。
Anaconda基本操作
# 查看conda版本号
conda -V
# 1.查看当前Anaconda的系统配置信息
conda info
# 2.列出当前系统中所有虚拟环境
conda env list
# 3.创建虚拟环境
conda create -n <虚拟环境名称> python=<python版本号> <包名1>==<版本 号> <包名2> ...
-- 创建new虚拟环境,指定各种软件版本,如果服务器没有,创建虚拟环境会自动下载,不需要再手动添加了
conda create -n new python=3.8.5 django==2.2
# 4.克隆虚拟环境
conda create -n <新的虚拟环境名称> --clone <旧的虚拟环境名称>
# 5.进入/切换到指定名称的虚拟环境,如果不带任何参数,则默认回到全局环境base 中。
conda activate <虚拟环境名称>
# 6.退出当前虚拟环境
conda deactivate
# 7.删除指定虚拟环境
conda remove -n <虚拟环境名称> --all
# 8.导出当前虚拟环境的Anaconda包信息到环境配置文件environment.yaml中
conda env export > environment.yaml
# 9.根据环境配置文件environment.yaml的包信息来创建新的虚拟环境
conda env create -f environment.yaml
部署/迁移
### 以下命令,和Anaconda没半毛钱关系,和项目部署/迁移有关。
pip freeze # 这里列出的是我们手动安装的包
pip list # 这里列出的不仅有我们手动安装的包,还有虚拟环境运行的依赖包
# 导出当前虚拟环境中的所有包并记录到requirements.txt文件中
pip freeze > requirements.txt
pip list --format=freeze > requirements.txt
# 往当前虚拟环境中导入requirements.txt文件中记录的所有包。
pip install -r requirements.txt
创建flask项目
# 1.创建flask虚拟环境
conda create -n flask python=3.8.5
# 2.安装flask
pip install flask==1.1.4
# 3.创建项目
创建项目目录 flaskdemo,在目录中创建manage.py.在pycharm中打开项目并指定上面创建的虚拟环境
创建一个flask框架的主程序。名字可以是app.py/run.py/main.py/index.py/manage.py
manage.py
# 导入Flask类
from flask import Flask
# 初始化
"""
import_name Flask程序所在的包(模块),传 __name__ 就可以
其可以决定 Flask 在访问静态文件时查找的路径
static_path 静态文件访问路径(不推荐使用,使用 static_url_path 代替)
static_url_path 静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder 静态文件存储的文件夹,可以不传,默认为 static
template_folder 模板文件存储的文件夹,可以不传,默认为 templates
"""
app = Flask(__name__)
# 编写路由视图
# flask的视图函数,flask中默认允许通过return返回html格式数据给客户端
@app.route('/')
def index():
return 'hello <h1>world!!</h1>'
if __name__ == '__main__':
# 运行flask,IP和端口,进入调试模式
app.run(host='0.0.0.0',port=5000,debug=True)
2.路由的基本定义
什么是路由?
路由就是一种映射关系。是绑定应用程序和url地址的一种一对一的映射关系!我们在开发过程中,编写项目时所使用的路由往往是指代了框架/项目中用于完成路由功能的类,这个类一般就是路由类,简称路由。
url中可以传递路由参数:两种方式
1.任意路由参数接收
# 参数传递,没有限定类型
@app.route('/<user_id>')
def index(user_id):
return 'hello %s'% user_id
2.限定路由参数接收
限定路由参数的类型,flask系统自带转换器编写在werkzeug.routing.py
文件中
转换器名称 | 描述 |
---|---|
string | 默认类型,接受不带斜杠的任何文本 |
int | 接受正整数 |
float | 接受正浮点值 |
path | 接收string 但也接受斜线 |
uuid | 接受UUID(通用唯一识别码)字符串 xxxx-xxxx-xxxxx-xxxxx |
# flask提供了路由转换器可以让我们对路由参数进行限定
# 路由传递参数[限定数据类型]
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
# 限定参数值范围
@app.route('/user/<int(min=10,max=100):user_id>')
def user_info(user_id):
return 'hello %d' % user_id
自定义路由参数转换器
也叫正则匹配路由参数.
# 1.引入flask内置的路由转换器父类
from werkzeug.routing import BaseConverter
# 2.创建自定义路由转换器,继承父类
class RegexConverter(BaseConverter):
def __init__(self,map,*args):
super().__init__(map)
# self.regex = args[0]
self.regex = "1[3-9]\d{9}"
# 3.把自定义转换器添加到flask默认的转换器字典中,名称为re,也就是和原来的int,float等放在一块
app.url_map.converters['re'] = RegexConverter
# 4.调用自定义转换器名称
# @app.route(rule='/user/<re("1[3-9]\d{9}"):phone>')
@app.route(rule='/user/<re:phone>')
def user(phone):
print(app.url_map)
return phone
路由限定请求方式
from flask import Flask,request
@app.route('/<int:user_id>',methods=["post","put","get","delete","patch"])
def index(user_id):
print(request.method) # 获取本次客户端的http请求方法
print(request.query_string) # 获取本次客户端的查询字符串
print(request.path) # 获取本次客户端请求的路由路径部分[去掉域名端口]
print(request.url) # 获取本次客户端请求的http url地址
return 'hello %s'% user_id
# 例如,post请求, http://127.0.0.1:5000/4378?username=jyh
POST
b'username=jyh'
/4378
http://127.0.0.1:5000/4378?username=jyh
3.http的请求与响应
请求 - request
常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求体的数据,并转换为字符串 只要是通过其他属性无法识别转换的请求体数据,最终都是保留到data属性中 例如:有些公司开发小程序,原生IOS或者安卓,这一类客户端有时候发送过来的数据就不一样是普通的表单,查询字符串或ajax |
bytes类型 |
form | 记录请求中的html表单数据 | MultiDict |
args | 记录请求中的查询字符串,也可以是query_string | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件列表 | * |
json | 记录ajax请求的json数据 | json |
获取请求中各项数据
from flask import Flask,request
from urllib.parse import parse_qs
# 初始化
app = Flask(__name__)
# 获取查询字符串
@app.route('/')
def index():
print(request.query_string) # 获取原生查询参数 username=jyh&pwd=123&pwd=abc
print( parse_qs( request.query_string.decode() ) ) # 解析
print(request.args) # 获取解析后的查询字符串
"""
请求地址:127.0.0.1:5000/?username=jyh&pwd=123&pwd=abc
打印效果:ImmutableMultiDict([('username', 'jyh'), ('pwd', '123'), ('pwd', 'abc')])
字典本身不支持同名键的,ImmutableMultiDict类解决了键同名问题
"""
# 获取指定的查询参数
print(request.args['username']) #jyh
print(request.args.get('username')) #jyh
print(request.args.getlist('pwd')) #['123', 'abc'] 参数同名的都能列出来
# 获取字典类型查询参数
print(request.args.to_dict(flat=True)) # {'username': 'jyh', 'pwd': '123'}
print(request.args.to_dict(flat=False)) # {'username': ['jyh'], 'pwd': ['123', 'abc']}
return 'hello world!'
# 获取请求数据
@app.route(rule='/data',methods=['post','put','patch'])
def data():
# 1.获取请求体数据,是其他属性无法接收到的数据
print(request.data)
# 2.获取form表单数据
print(request.form) # 元组类型
print(request.form.get('name'))
# 3.获取json数据 - ajax上传的数据或其他json数据
print(request.json)
print(request.json.get('name'))
# 4.获取file文件数据
print(request.files) # ImmutableMultiDict([('avatar', <FileStorage: '1.webp' ('image/webp')>)])
print(request.files['avatar']) # <FileStorage: '1.webp' ('image/webp')>
print(request.files.get('avatar')) # <FileStorage: '1.webp' ('image/webp')>
# 5.获取请求头信息
print(request.headers)
print(request.headers.get('Host')) # 127.0.0.1:5000
"""
请求头信息
Content-Type: application/json
User-Agent: PostmanRuntime/7.26.10
Accept: */*
Postman-Token: 37e819e7-38db-4b47-b5ab-059cfaffd2d4
Host: 127.0.0.1:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 20
"""
# 6.获取请求的URL地址
print(request.url) # http://127.0.0.1:5000/data
print(request.path) # /data
print(request.remote_addr) # IP地址 127.0.0.1
return 'ok'
# 声明和加载配置 - 开启调试模式
class Config():
DEBUG = True
app.config.from_object(Config)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
响应
flask默认支持两种响应方式:
# 1.数据响应 - make_response jsonify
默认响应html文本,也可以返回 JSON格式,或其他格式
# 2.页面响应: 重定向 - redirect
url_for: 视图之间的跳转,获取视图对应的url地址,在使用redirect跳转页面
# 3.响应的时候,flask也支持自定义http响应状态码
响应各种数据
from flask import Flask,request,make_response,redirect,jsonify,url_for
# 初始化
app = Flask(__name__)
# 1.响应html文本数据 - make_response
@app.route(rule='/')
def index():
# 默认支持响应HTML文本
# return '<a href="https://www.baidu.com/">跳转百度</a>'
return make_response('<a href="https://www.baidu.com/">跳转百度</a>')
# 2.响应json数据 - jsonify
@app.route(rule='/json')
def json():
data = [
{"name":"jyh","age":11},
{"name":"sxy","age":12},
{"name":"gzs","age":13},
]
# flask中返回json 数据,都是flask的jsonify方法返回就可以了
return jsonify(data)
# 3.响应自定义格式数据 - 必须定义响应类型
'''响应图片类型数据'''
@app.route(rule='/image')
def image():
with open('1.webp','rb')as fp:
content = fp.read()
response = make_response(content)
response.headers['content-type'] = 'image' #定义响应类型 MIME type 其他类型网上搜
return response
# 4.重定向
'''4.1重定向到百度页面或自己页面 - 路径'''
@app.route(rule='/bai')
def dai():
# return redirect('https://www.baidu.com/')
return redirect('/json')
'''
4.2重定向到自己写的json数据页面
使用url_for可以实现视图方法之间的内部跳转
url_for("视图方法名")
仅仅是根据参数从app.url_map中提取对应函数的url地址
'''
@app.route(rule='/gojson')
def gojson():
url = url_for('json')
print(url) # /json
return redirect(url)
'''4.3重定向到带有参数的视图函数'''
@app.route(rule='/user/<user_id>')
def user(user_id):
return f'user_id = {user_id}'
@app.route(rule='/gouser')
def gouser():
# return redirect(url_for(endpoint='user',user_id=123))
return redirect(url_for('user',user_id=123))
# 5.自定义状态码和响应头
'''自定义状态码'''
@app.route(rule='/status')
def status():
return 'status = 666',400
'''自定义状态码和响应头'''
@app.route(rule='/respon')
def respon():
response = make_response('ok')
print(response) # <Response 2 bytes [200 OK]>
response.headers['Hehe'] = '666' # 自定义响应头
response.status_code = 201 # 自定义响应状态码
return response
# 声明和加载配置 - 开启调试模式
class Config():
DEBUG = True
app.config.from_object(Config)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
4.http会话控制
所谓的会话,就是客户端浏览器和服务端网站之间一次完整的交互过程.
会话的开始是在用户通过浏览器第一次访问服务端网站开始.
会话的结束时在用户通过关闭浏览器以后,与服务端断开.
所谓的会话控制,就是在客户端浏览器和服务端网站之间,进行多次http请求响应之间,记录、跟踪和识别用户的信息而已。
因为 http 是一种无状态协议,浏览器请求服务器是无状态的。
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。
有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
实现状态保持主要有两种方式:
- 在客户端存储信息使用
Cookie,token[jwt,oauth]
- 在服务器端存储信息使用
Session
补充:
token是一种用户身份认证信息的技术,一般我们称之为:Token认证。
翻译中文:token一般叫令牌
本质就是一个经过特殊处理的字符串,往往在字符串内部隐藏着识别用户身份信息的关键内容。
一般开发中,token往往都是以识别用户身份为目的来使用的。
一般使用情况下,token会以用户身份信息,当前事件戳,随机数等因子构成的。
当然,更多情况下,token一般分三段:"头部.载荷.签证"
像实际开发中,我们一般说的jwt,csrf等等场景里面的token都是这一类的。
cookie
Cookie是由服务器端生成,发送给客户端浏览器,浏览器会自动将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。
使用场景: 登录状态, 浏览历史, 网站足迹, 购物车 [不登录也可以使用购物车]
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
Cookie基于域名安全,不同域名的Cookie是不能互相访问的
浏览器的同源策略针对cookie也有限制作用.
当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
session
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
在服务器端进行状态保持的方案就是Session
Session依赖于Cookie,session的ID一般默认通过cookie来保存到客户端。
设置/获取cookie - 设置/获取session
from flask import Flask,request,make_response,session
# 初始化
app = Flask(__name__)
# 1.设置cookie - 通过response响应对象设置
@app.route(rule='/set_cookie')
def set_cookie():
response = make_response('set cookie')
response.set_cookie('username','jyh') # 过期时间是会话期,关闭浏览器以后,自动删除
# response.set_cookie('username','jyh',max_age=3600) # 在指定max_age时间以后,才会自动删除
response.set_cookie('username','jyh',3600)
return response
# 2.获取cookie - 通过request获取
@app.route(rule='/get_cookie')
def get_cookie():
res = request.cookies.get('username')
return res
# 3.删除cookie - 通过设置cookie过期时间,让浏览器删除cookie
@app.route(rule='/delete_cookie')
def delete_cookie():
response = make_response()
# response.set_cookie('username','',max_age=0) # 0或负数 代表过期
response.set_cookie('username','',0)
return response
# 4.设置session
# 加载配置 - 使用session之前必须配置SECRET_KEY秘钥,否则报错,秘钥是随机字符串
class Config():
DEBUG = True
SECRET_KEY = "jyh123"
app.config.from_object(Config)
@app.route(rule='/set_session')
def set_session():
session['username'] = 'jyh'
return 'set_session'
# 5.获取session
@app.route(rule='/get_session')
def get_session():
return session.get('username')
# 6.删除session
@app.route(rule='/del_session')
def del_session():
# 先判断是否有session值,再删除,否则报错
if session.get('username'):
def session['username']
return 'del_session'
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)