HTTP通讯过程
浏览器 -> HTTP请求
-> TCP传输 -> 服务器
-> 接收请求 -> 解析报文
-> 路由分发 -> 执行视图函数
-> 响应报文 -> HTTP响应
-> 浏览器
前端和后端
前端:
-浏览器
-手机app
-爬虫
-程序urllib urllib2 ajax
后端:
服务器 + WSGI + web框架程序(路由+视图)
uWSGI Django(进行路由分发,定义视图函数)
process=4
thread=2
服务器模型(并发)
-多进程
-多线程
-协程
web框架程序
重量级框架:Django
轻量级框架:Flask, Tornado
Django
提供了一站式解决方案
集成了 MVT(model view template)
ORM
Flask
路由:Werkzeug工具箱
模板:Jinja2
Flask扩展包
Flask-SQLalchemy 操作数据库
Flask-migrate 迁移数据库
Flask-Mail 邮件
Flask-WTF 表单
Flask-script 脚本
Flask-Login 认证用户状态
Flask-RESTful rest api
Flask-Bootstrap 前端框架Bootstrap
Flask-Moment 本地化日期和时间
虚拟环境
如果使用sudo就不是当前虚拟环境了
-p path 路径
pip freeze > requirement.txt
pip install -r requirement.txt
Flask配置启动
不要使用from flask import *
__name__
当前模块名字
flask默认传入文件名所在目录为根目录,找不到模块则当前文件所在目录为根目录
1、Flask初始化参数
import_name 导入路径 static_url_path 静态资源url前缀 static_folder 静态资源目录 默认static template_folder 模板文件目录 默认templates
2、Falsk配置参数设置
# (1)使用配置文件 DEBUG=True > config.cfg app.config.from_pyfile("config.cfg") # (2)使用对象配置参数 class Config(Object): DEBUG=True app.config.from_object(Config) # (3)直接操作config字典对象 app.config["DEBUG"] = True
3、Falsk配置参数读取
# (1)直接操作config字典对象 app.config.get("DEBUG") # (2)通过current_app获取 current_app.config.get("DEBUG")
4、Falsk启动程序
局域网和外网都可以访问
app.run(host="0.0.0.0", port=5000, debug=True)
路由视图
1、查看flask路由信息
app.url_map
2、设置请求方式
@app.route("/", methods=["GET", "POST"])
路由相同,路径和请求方式相同,先定义先访问
路由不同,路径和请求方式不同
3、一个视图多个路由
不同路由可以通过装饰器叠加,添加到一个视图函数之上
@app.route("/1") @app.route("/2") def view(): pass
4、重定向
from flask import redirect, url_for # 视图反推路径 url = url_for("index") # 重定向 redirect("/")
5、路由参数提取
@app("/<int:uid>") def view(uid): pass # 接受参数:默认string(除了斜线/), int, float, path
6、自定义万能路由转换器
# (1)继承转换器类 from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): def __init__(self, url_map, regex): # 调用父类的初始化方法 super(RegexConverter, self).__init__(url_map) # 将正则表达式的参数保存到对象属性中 # flask会去使用这个属性进行路由的正则匹配 self.regex = regex def to_python(self, value): return value def to_url(self, value): return value # (2)将自定义的转换器添加到flask应用中 app.url_map.converters["re"] = RegexConverter # (3)使用自定义转换器路由匹配 to_python #/13512345678 @app.route("/<re(r'1[34578]\d{9}'):mobile>") def send(mobile): pass # (4)视图转换为路由url to_url @app.route("/") def index(): url = url_for("send", mobile="13511111111") return redirect(url)
request对象
from flask import request
属性
data 请求体数据(除表单外) string
form 表单数据 MultiDict
args url查询参数 MultiDict
cookies cookies信息 Dict
headers 报文头 EnvironHeaders
method HTTP方法 GET/POST
url url地址 string
files 上传的文件 *
前端参数
(1)form表单格式 POST
application/x-www-form-urlencoded
name=“Tom”&age=23
request.form.get() 同键多值取第一个
request.form.getlist() 取出同键所有值
(2)多媒体格式 POST
enctype=“multipart/form-data”
file = request.files.get(“file”)
file.save(“file.txt”)
(3)raw字符串
(4)binary
(5)args查询字符串 QueryString GET
?key=value&key=value
request.args.get()
python2字符串类型
a=“中国” str “utf-8” “gbk” “ascii”
a=u"中国" unicode
“中国 %s” % “abc”
ASCII cannot decode \xxx\xxx
->
u"中国 %s" % “abc”
上下文管理器
with open("test.txt", "w") as f: f.write("something")
自定义上下文管理器
class Foo(object): def __enter__(self): pass def __exit__(self, exc_type, exc_value, exc_traceback): pass with Foo() as f: pass
错误处理
1、abort方法
可以终止视图函数执行,并返回给前端特定信息
from flask import abort # (1)传递标准状态码信息 abort(400) # (2)传递响应体信息 from flask import Response abort(Response("message"))
2、自定义错误处理
@app.errorhandler(404) def handle_404_error(err): return ""
响应信息
1、元组
响应体, 状态码(可以是非标), 响应头
return "message", 404, [("language": "python")] return "message", 404, {"language": "python"} return "message", "404 status message", {"language": "python"}
2、make_response
from flask import make_response resp = make_response("message") # 响应体 resp.status = "666 status" # 状态码 resp.headers["language"] = "python" # 响应头 return resp
3、返回json
json就是字符串
原理:json.dumps() + Content-Type: application/json
#(1)方式一 return jsonify({"name": "Tom"}) #(2)方式二 return jsonify(name=Tom")
cookie
1、设置cookie
默认临时cookie,浏览器关闭就失效,max_age设置有效期,单位:秒s
from flask import make_response resp = make_response("message") #(1)通过原始方式设置 resp.set_headers["Set-Cookie"] = "key=value;" #(2)通过flask提供的方式设置 resp.set_cookie("key", "value", max_age=3600) return resp
2、获取cookie
from flask import request value = request.cookies.get("key")
3、删除cookie
设置cookie的过期时间为当前时间,让浏览器自己删除
from flask import make_response resp = make_response("message") resp.delete_cookie("key", "value") return resp
session
广义:机制
狭义:保存到服务器中的session数据
浏览器 <- cookie(session_id) -> 服务器 <—> 数据库MySQL
session保存的地方:
MySQL、Redis、MongoDB、文件、程序内存(不支持多服务器)
flask默认把session保存到了cookie中, 不安全
只使用session_id
(1)保存到cookie
(2)保存到url中,具有局限性,适用于单次访问
# 1、设置session秘钥, 加密混淆,防止session被篡改 app.config["SECRET_KEY"] ="ooxx" # 2、设置session数据 from flask import session session["key"] = "value" # 3、获取session数据 value = session.get("key")
请求上下文与应用上下文
(1)请求上下文
request, session对象都是全局对象
全局变量 - 线程局部变量
(2)应用上下文
current_app 当前运行程序文件的程序实例
g 处理请求时,用于临时存储的对象,每次请求都会重置,用于一次请求多个函数传参
g.name = "张三"
请求钩子hook
类似Django中的中间件
1、处理第一个请求前执行 @app.before_first_request def handle(): pass 2、处理每个请求前执行 @app.before_request def handle(): pass 3、处理每个请求后执行,视图函数没有出现异常执行 @app.after_request def handle(response): return response 4、处理每个请求后执行,视图函数是否出现异常都执行, 非调试模式debug=False @app.teardown_request def handle(response): return response
使用
python app.py runserver -h 0.0.0.0 -p 8000
python app.py shell
jinja2模板
1、渲染
from flask import render_template data = { "name": "python", "age": 18, "my_dict": {"city": "beijing"}, "my_list": [1, 2, 3, 4, 5], } render_template("index.html", **data)
变量 {undefined{ name }}
字典 {undefined{ my_dict.city }} {undefined{ my_dict[“city”] }}
列表 {undefined{ my_list[0] }}
数字相加 {undefined{ 1 + 2 }}
字符串拼接 {undefined{ “hello” + “world” }}
2、 字符串过滤器(支持链式调用 )
safe 禁用转义 xss攻击
capitalize 首字母大写,其他小写
lower 转小写
upper 转大写
title 每个单词首字母大写
trim 首尾空格去掉
reverse 反转字符串
format 格式化输出(类似%格式化)
striptags 删除html标签
3、列表过滤器
first 取第一个元素
last 取最后一个元素
length 获取列表长度
sum 列表求和
sort 列表排序
4、自定义过滤器
方式一:
(1)定义过滤器 def list_step_2(lst): return lst[::2] (2)注册过滤器 app.add_template_filter(list_step_2, "list_step_2") (3)使用过滤器 {{ lst | list_step_2 }}
方式二:
@app.template_filter("list_step_2") def list_step_2(lst): return lst[::2]
模板宏macro
1、不带参数宏
{% macro input() %} <input type="text" > {% endmacro %} {{ input() }}
2、带参数宏
{% macro input(type) %} <input type="{{type}}" > {% endmacro %}
3、带默认参数宏
{% macro input(type="text") %} <input type="{{type}}" > {% endmacro %}
4、导入宏
{% import "macro.html" as my_macro%} {{ my_macro.input() }}
模板继承
bash.html
{% block top%} 父模板的内容 {% endblock %}
child.html
{% extends "bash.html" %} {% block top%} 子模板的内容 {% endblock %}
模板包含
{% inclued "hello.html" %}
Flask中的特殊变量
可以直接在模板中使用
config
request
url_for
get_flashed_messages基于session
from flask import flash flash("hello1") flash("hello2") {% for msg in get_flashed_messages() %} {{msg}} {% endfor %}
Flask-Script扩展
pip install Flask-Script
Manager 启动命令管理类
from flask import Flask from flask_script import Manager app = Flask(__name__) #创建app管理类 manager = Manager(app) @app.route("/") def index(): pass if __name__ == "__main__": # app.run() manager.run()
表单扩展
pip install Flask-WTF
可以进行csrf验证 表单生成 和 验证
Flask数据库扩展
pip install Flask-SQLAlchemy
Flask数据库迁移扩展
pip install Flask-Script (依赖) pip install Flask-Migrate
Flask发送邮件
pip install Flask-Mail
循环导入-死锁
解决:一方让步,延迟导入
路由设置的方式
1、装饰器 @app.route("/") def view(): pass 2、装饰器原始方式 def view(): pass app.route("/")(view)
蓝图Blueprint
一个小模块的抽象概念, 延迟加载
1、定义蓝图
# __init__.py from flask import Blueprint # 工程目录的优先级大于应用目录 app_user = Blueprint("app_user", __name__, template_folder="templates") # init执行的时候把视图加载进来 from .views import view
2、定义视图
# views.py from . import app_user @app_user.route("/") def view(): pass
3、注册蓝图
# main.py from flask import Flask app = Flask(__name__) app.register_blueprint(app_user, url_prefix="/user")
4、查看路由
app.url_map
单元测试
web开发阶段:
需求分析,设计阶段,实现阶段,测试阶段
测试阶段
单元测试,集成测试,系统测试
断言assert
-真 继续执行
-假 抛出异常 AssertionError
Flask单元测试
app.test_client()
测试数据库
开启->测试->断开
Flask部署
pip install gunicorn
gunicorn -w 4 // 进程数 -b 127.0.0.1:5000 // 开启端口 --access-logfile ./logs //日志文件 -D // 守护进程方式运行 main:app // 要运行的程序入口
ps aux|grep gunicorn
Nginx
负载均衡
静态文件
cp nginx.conf nginx.conf.bak
upstream flask{ server 10.0.0.1:5000; server 10.0.0.1:5001; } server{ listion 80; server_name localhost; location / { proxy_pass http://flask; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr }
平滑重启 nginx -s reload
mysql异常
@@tx_isolation
# cursor.execute("SELECT @@tx_isolation") if self.server_version_info < (5, 7, 20): cursor.execute("SELECT @@tx_isolation") else: cursor.execute("SELECT @@transaction_isolation")