欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flask基础知识点总结(一),新手必看!!!

程序员文章站 2022-07-15 11:01:47
...
Flask基础分为三个阶段,此版本为你第一版,后续请各位同学继续关注!!!

#1Flask的搭建环境及使用,以及视图函数的用法
使用pip install Flask 指令进行扩展包的安装
在Pycharm中使用 from flask import Flask进行导入
基本的模型为:
from flask import Flask 

app = Flask(__name__)

@app.route('/')#指定路由地址
def index():
    
    return 'hello world'
if '__name__'=='__main__':
    app.run()#启动服务
#2视图的常用逻辑
2-1 返回JSON
#在使用 Flask 写一个接口时候需要给客户端返回 JSON 数据,
#在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应

# 返回JSON
@app.route('/demo4')
def demo4():
    json_dict = {
        "user_id": 10,
        "user_name": "laowang"
    }
    return jsonify(json_dict)
2-2 重定向 使用redirect模块

@app.route(/123)
def demo()return redirect('http://www.baidu.com')
    
# 异常捕获
HTTP 异常主动抛出,abort方法‘abort(500),给定一个状态码
捕获错误 errorhandler
from flask import Flask 

app = Flask(__name__)

@app.errorhandler(500)
def index(e):
    return '服务器找不到了'
@app.errorhandler(ZeroDivisionError)
def zero(e):
    return '除数不能为0'
if '__name__'=='__main__':
    app.run()

Cookie

客户端和服务端进行会话保持的一种技术

cookies保存在客户端,具体的数据是每次访问服务器后,服务器通过响应头的set_cookies字段告诉浏览器,需要将指定的数据保存在cookies中,并且浏览器下一次发起请求时,会自动将cookies中的数据写入请求头的Cookie字段中传给浏览器

cookies的存储具有一定的有效期,并且时同源策略(浏览器只会携带百度的cookies传给百度服务器)

user_data = {}


@app.route("/")
def index():
# 获取cookie
id = request.cookies.get("user_id")
if id:
    name = user_data[id]
    return "welcome %s" % name
else:
    return "请登陆"


@app.route("/user", methods=['POST', "GET"])
def user():
# 获取参数
    name = request.args.get('name', "")
    id = request.args.get("id", "")

# 保存数据
    user_data[id] = name

# 构造响应
    resp = make_response("登陆完成")
# 设置cookie
     resp.set_cookie("user_id", id)

        return resp

session

将用户信息直接存储在Cookie中并不安全,可以考虑将用户信息存储到服务器中,并且加密存储,只把存储位置放入cookies中,以后浏览器通过携带cookies中存储位置找到对应数据

from flask import session

使用session必须设置secret_key

app.secret_key = "itcast"

@app.route("/")
def index():
    session['user_name'] = "张三"
    session['id'] = 123
    return "success"

勾子函数

#Flask支持四种请求勾子
<!--# before_first_request:-->
<!--在处理第一个请求前执行-->

<!--before_request:-->
<!--在每次请求前执行-->
<!--如果在某修饰的函数中返回了一个响应,视图函数将不再被调用-->

<!--after_request:-->
<!--如果没有抛出错误,在每次请求后执行-->
<!--接受一个参数:视图函数作出的响应-->
<!--在此函数中可以对响应值在返回之前做最后一步修改处理-->
<!--需要将参数中的响应在此参数中进行返回-->

<!--teardown_request:-->
<!--在每次请求后执行-->
<!--接受一个参数:错误信息,如果有相关错误抛出-->
from flask import Flask
from flask import abort

app = Flask(__name__)


# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
    print("before_first_request")


# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
    print("before_request")
    # if 请求不符合条件:
    #     return "laowang"


# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
    print("after_request")
    response.headers["Content-Type"] = "application/json"
    return response


# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
    print("teardown_request")


@app.route('/')
def index():
    return 'index'

if __name__ == '__main__':
    app.run(debug=True)

自定义路由转化器

创建一个类,继承自BaseConverter

重写类的regex属性

添加到converters中:app.url_map.converters[‘re’] = ReConverter

创建一个通用的路由转化器

class ReConverter(BaseConverter):

def __init__(self, map, regex):
    super(ReConverter, self).__init__(map)
    self.regex = regex

app.url_map.converters['re'] = ReConverter

@app.route("/user/<re('[0-9]{2}'):user_id>")

def user(user_id):

    return "用户id %s" % user_id


@app.route("/user/<re('[a-z]{6}'):user_name>")
def user2(user_name):

    return "用户name %s" % user_name

CSRF攻击:跨站请求伪造,

1.用户在登陆存在漏洞的A网站后,网站会向用户的浏览器写入登陆凭证到Cookie中

2.用户在没有退出A网站的前提下,访问了B网站
B网站伪造了一个form表单,参数被设置为固定值,单action指向了A网站,当用户点击提交的时候,浏览器会自动向A网站发起请求,并携带伪造的参数信息和Cookies

3.A网站根据Cookies和参数信息做出响应
如果防止CSRF攻击?

4.A网站在下发form表单的时候,产生一个随机值放入表单中作为隐藏字段,同时将这个随机值写入Cookis

5.A网站在验证请求时,必须从form中能够取出隐藏字段的值,以及从cookies中也能取出写入的值,并且二者相同,认为是合法请求
在不使用flask-wtf表单的情况下,进行CSRF保护

集成flask-wtf

手动在form表单中设置隐藏字段,字段name为csrf_token


在上述代码中,由于以下原因,所以模板中的csrf_token()有效

app.jinja_env.globals[‘csrf_token’] = generate_csrf
在代码中开启保护

from flask_wtf import CSRFProtect
### 设置seeesion的**
app.secret_key = "itcast"
### 开启保护
CSRFProtect(app)
相关的配置参数

WTF_CSRF_ENABLED设置是否进行CSRF验证
WTF_CSRF_METHODS设置哪些方法的请求会进行CSRF验证,默认是['POST', 'PUT', 'PATCH', 'DELETE']
WTF_CSRF_FIELD_NAME 获取提交的csrf_token的值对应的字段名称
源码分析:

给seesion中设置一个csrf_token值
{"csrf_token":"d00418ddd6b434101c90fecf9268215774b82965"}

并将其进行base64加密后设置为G变量的csrf_token属性的值,并返回

def generate_csrf(secret_key=None, token_key=None):
# 获取**
secret_key = _get_config(
    secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
    message='A secret key is required to use CSRF.'
)
# 获取存储csrf_token值的键
field_name = _get_config(
    token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token',
    message='A field name is required to use CSRF.'
)

if field_name not in g:
    if field_name not in session:
        """
        在session中设置一串64位长度的字符串并进行hashlib.sha1加密,返回一个40位长度的字符串作为token值
       
        """
        session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest()

    s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token')
    # 给g变量设置一个属性,csrf_token=Base64(token值)
    setattr(g, field_name, s.dumps(session[field_name]))

return g.get(field_name)

当用户提交form表单时,会进行验证:
@app.before_request
def csrf_protect():
if not app.config[‘WTF_CSRF_ENABLED’]:
return

    if not app.config['WTF_CSRF_CHECK_DEFAULT']:
        return
	# 只会验证post请求,get请求直接放行
    if request.method not in app.config['WTF_CSRF_METHODS']:
        return

    if not request.endpoint:
        return

    view = app.view_functions.get(request.endpoint)

    if not view:
        return

    if request.blueprint in self._exempt_blueprints:
        return

    dest = '%s.%s' % (view.__module__, view.__name__)

    if dest in self._exempt_views:
        return

    self.protect()
def protect(self):
    if request.method not in current_app.config['WTF_CSRF_METHODS']:
        return

    try:
        # 从form表单中获取name为csrf_token的值和seesion中存储的csrf_token进行对比验证
        validate_csrf(self._get_csrf_token())
    except ValidationError as e:
        logger.info(e.args[0])
        self._error_response(e.args[0])

    if request.is_secure and current_app.config['WTF_CSRF_SSL_STRICT']:
        if not request.referrer:
            self._error_response('The referrer header is missing.')

        good_referrer = 'https://{0}/'.format(request.host)

        if not same_origin(request.referrer, good_referrer):
            self._error_response('The referrer does not match the host.')

def validate_csrf(data, secret_key=None, time_limit=None, token_key=None):

secret_key = _get_config(
    secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
    message='A secret key is required to use CSRF.'
)
field_name = _get_config(
    token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token',
    message='A field name is required to use CSRF.'
)
time_limit = _get_config(
    time_limit, 'WTF_CSRF_TIME_LIMIT', 3600, required=False
)

if not data:
    raise ValidationError('The CSRF token is missing.')

if field_name not in session:
    raise ValidationError('The CSRF session token is missing.')

s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token')

try:
    # 将form表单的中csrf_token的值进行base64解码,返回的时一个40位长度的字符串
    token = s.loads(data, max_age=time_limit)
except SignatureExpired:
    raise ValidationError('The CSRF token has expired.')
except BadData:
    raise ValidationError('The CSRF token is invalid.')
# 将token和sesison中的csrf_token的值进行对比
if not safe_str_cmp(session[field_name], token):
    raise ValidationError('The CSRF tokens do not match.')

模板的使用 template

1.在项目下创建 templates 文件夹,用于存放所有的模板文件,并在目录下创建一个模板html文件 temp_demo1.html

2.设置 templates 文件夹属性以便能够在代码中有智能提示

3.设置 html 中的模板语言,以便在 html 有智能提示

4.创建视图函数,将该模板内容进行渲染返回

@app.route('/')
def index():
    return render_template('temp_demo1.html')

5.代码中传入字符串,列表,字典到模板中

@app.route('/')
def index():
    # 往模板中传入的数据
    my_str = 'Hello 黑马程序员'
    my_int = 10
    my_array = [3, 4, 2, 1, 7, 9]
    my_dict = {
        'name': 'xiaoming',
        'age': 18
    }
    return render_template('temp_demo1.html',
                           my_str=my_str,
                           my_int=my_int,
                           my_array=my_array,
                           my_dict=my_dict
                           )

6.模板中的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
我的模板html内容
<br/>{{ my_str }}
<br/>{{ my_int }}
<br/>{{ my_array }}
<br/>{{ my_dict }}

</body>
</html>

Jinjia2 中的If语句和for循环(控制代码块)

if语句
{%if user.is_logged_in() %}
    <a href='/logout'>Logout</a>
{% else %}
    <a href='/login'>Login</a>
{% endif %}
过滤器可以被用在 if 语句中:
{% if comments | length > 0 %}
    There are {{ comments | length }} comments
{% else %}
    There are no comments
{% endif %}

循环

Jinja2 中使用循环来迭代任何列表或者生成器函数
{% for post in posts %}
    <div>
        <h1>{{ post.title }}</h1>
        <p>{{ post.text | safe }}</p>
    </div>
{% endfor %}
循环和if语句可以组合使用,以模拟 Python 循环中的 continue 功能,下面这个循环将只会渲染post.text不为None的那些post:
{% for post in posts if post.text %}
    <div>
        <h1>{{ post.title }}</h1>
        <p>{{ post.text | safe }}</p>
    </div>
{% endfor %}

模板继承 extends

父模板
#base.html
{% block top %}
  顶部菜单
{% endblock top %}

{% block content %}
{% endblock content %}

{% block bottom %}
  底部
{% endblock bottom %}
子模板
{% extends 'base.html' %}#extends声明这个模板继承自哪
{% block content %}
 需要填充的内容
{% endblock content %}

蓝图,readis

蓝图

1.创建一个蓝图对象

admin=Blueprint('admin',__name__)

2.在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器

@admin.route('/')
def admin_home():
    return 'admin_home'

3.在应用对象上注册这个蓝图对象

  app.register_blueprint(admin,url\_prefix='/admin')
Readis非关系型数据库/基础指令的操作/与python的交互
readis的数据操作

1.String

```python
设置键值
set key value
set name itlgg
eg:设置键名name值为itgg

设置键值及过期时间,以秒为单位
setx key seconds value
setx aa 3 aa
eg:设置键为aa值为aa过期时间为3秒的数据

设置多个键值
mset key1 value1 key2 value
mset a1 python a2 java a3 c
eg:设置键为'a1'值为'python'、键为'a2'值为'java'、键为'a3'值为'c'


追价值
append key value
append 'a1' 'haha'
eg:向键为a1中追加值' haha'

```
获取
获取:根据键获取值,如果不存在此键则返回nil
get key
get 'name'
eg:获取键'name'的值



根据多个键获取多个值
mget key1 key2 ...
mget a1 a2 a3
eg:获取键a1、a2、a3'的值

2.set

添加元素
sadd key member1 member2 ...
sadd a3 zhangsan sili wangwu
eg:向键'a3'的集合中添加元素'zhangsan''lisi''wangwu'



返回所有的元素
smembers key
smembers a3
eg:获取键'a3'的集合中所有元素


删除指定元素
srem key
srem a3 wangwu
eg:删除键'a3'的集合中元素'wangwu'



相关标签: web初学者