• flask文档整理的笔记


    flask官方文档总结

    唯一的 URL / 重定向行为

    @app.route('/projects/')
    def projects():
        return 'The project page'
    
    @app.route('/about')
    def about():
        return 'The about page'
    
    
    projects 的 URL 是中规中矩的,尾部有一个斜杠,看起来就如同一个文件夹。 访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向,帮你在尾部加上一个斜杠。
    
    about 的 URL 没有尾部斜杠,因此其行为表现与一个文件类似。如果访问这个 URL 时添加了尾部斜杠就会得到一个 404 错误。这样可以保持 URL 唯一,并帮助 搜索引擎避免重复索引同一页面。
    

    url_for路由解析

    @app.route('/', methods=['GET'])
    def index():
    	# url_for 路由解析  第一个参数可以是  想跳转的函数的名字
    	# return redirect(url_for('func'))
    
    	return redirect(url_for('func',username='耿泽世'))
    
    
    @app.route('/index/<username>', methods=['GET'])
    def func(username):
    	print(username)
    	return '恭喜'
    -----------------------------------------------------------------------------------------
    
    @app.route('/', methods=['GET'])
    def index():
    	# url_for 路由解析  第一个参数可以是  想跳转的函数的名字
    	# return redirect(url_for('func'))
    
    	return redirect(url_for('func', next='nihoa'))
    
    
    @app.route('/index/', methods=['GET'])
    def func():
    	name = request.args.get('next')
    	print(name)
    	return '恭喜'
    

    flask的请求方法

    from flask import request
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            return do_the_login()
        else:
            return show_the_login_form()
    

    静态文件

    #在项目下创建一个static的文件夹,静态文件回去里面找,在Flask实例的是后参数里面是默认的
    这个参数是配置静态文件的参数   static_folder  在Flask的参数中
    
    
    @app.route('/', methods=['GET'])
    def index():
    	
    	return render_template('index.html')
    
    
    #html中的代码
    </head>
    <img src="/static/1.png" alt="">
    </html>
    
    #开启服务之后可以访问到图片
    

    渲染模板

    #建模板文件夹有两种方式
    
    #Flask 会在 templates 文件夹内寻找模板。因此,如果你的应用是一个模块, 那么模板文件夹应该在模块旁边;如果是一个包,那么就应该在包里面
    
    #情形1
    /application.py
    /templates
        /hello.html
        
    #情形2
    /application
        /__init__.py
        /templates
            /hello.html
            
            
            
            
    {% for item in navigation %}
            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
        {% endfor %}
        
     
    {{ foo.bar }}
    {{ foo['bar'] }}
    
    
    {% if True %}
            yay
    {% endif %}
    
    
    
    {%+ if something %}yay{% endif %}
    
    
    
    
    <head>
        {% block head %}
        <link rel="stylesheet" href="style.css" />
        <title>{% block title %}{% endblock %} - My Webpage</title>
        {% endblock %}
    </head>
    <body>
        <div id="content">{% block content %}{% endblock %}</div>
        <div id="footer">
            {% block footer %}
            &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
            {% endblock %}
        </div>
    </body>
    
    
    
    #模块
    {% block sidebar %}
        <h3>Table Of Contents</h3>
        ...
        {{ super() }}
    {% endblock %}
    
    
    #后端返回html代码是被包裹在Markup()中浏览器会识别html代码并且执行
    
    
    
    flask中request是如何实现全局独立的
    在介绍flask中的request之前,先简单介绍下python中的ThreadLocal对象。ThreadLocal,故名思义,就是线程中的全局变量,最近才接触这个概念,之前在C++和C#中还没遇到过这个东西。首先看下下面的代码:
    
            
    
    import threading
     
    localobj=threading.local()
     
    def threadfunc(name):
        localobj.name=name
        print 'localobj.name is %s' %name
     
    if __name__=='__main__':
        t1=threading.Thread(target=threadfunc,args=('Hyman',))
        t2=threading.Thread(target=threadfunc,args=('liuzhihui',))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
         localobj就是一个Threadlocal对象,他对于每个线程来说是全局的,但是对于不同线程之间又可以保持不同。而flask中的请求对象request就由是这类对象。在多线程服务器中客户端每建立一个链接,服务器就创建一个线程,每个线程中就会有一个request来表示客户端的链接请求信息。
    from flask import Flask
    from flask import request
     
    app=Flask(__name__)
     
     
    @app.route('/')
    def index():
        user_agent=request.headers.get('User_Agent')
        return 'user_agent is %s' %user_agent
     
    if __name__=='__main__':
    	app.run()
    

    请求对象

    from flask import request
    通过使用 method 属性可以操作当前请求方法,通过使用 form 属性处理表单数据(在 POST 或者 PUT 请求 中传输的数据)。以下是使用上述两个属性的例子:
        
    @app.route('/login', methods=['POST', 'GET'])
    def login():
        error = None
        if request.method == 'POST':
            if valid_login(request.form['username'],
                           request.form['password']):
                return log_the_user_in(request.form['username'])
            else:
                error = 'Invalid username/password'
        # the code below is executed if the request method
        # was GET or the credentials were invalid
        return render_template('login.html', error=error)
    
    
    当 form 属性中不存在这个键时会发生什么?会引发一个 KeyError 。
    
    
    获取get提交的数据
    要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性:
    searchword = request.args.get('key', '')
    

    文件上传

    用 Flask 处理文件上传很容易,只要确保不要忘记在你的 HTML 表单中设置 enctype="multipart/form-data" 属性就可以了。否则浏览器将不会传送你的文件
    
    可以通过请求对象 files 属性来访问上传的文件
    另外多出一个 用于把上传文件保存到服务器的文件系统中的 save() 方法
    
    #例子
    from flask import request
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/uploaded_file.txt')
            
            
    可以使用 filename 属性  可以获取文件名字
    
    
    但是请牢记这个值是 可以伪造的,永远不要信任这个值。如果想要把客户端的文件名作为服务器上的文件名, 可以通过 Werkzeug 提供的 secure_filename() 函数:
        
    from flask import request
    from werkzeug.utils import secure_filename
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/' + secure_filename(f.filename))
        ...
        
        
    #下面补充
    UPLOAD_FOLDER 是上传文 件要储存的目录
    #这样写
    app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
    
    ALLOWED_EXTENSIONS 是允许上传的文件扩展名的集合
    ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
    
    
    
    下一个函数检查扩展名是否合法,上传文件,把用户重定向到已上传文件的 URL:
    def allowed_file(filename):
        return '.' in filename and 
               filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
    
    @app.route('/', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            # check if the post request has the file part
            if 'file' not in request.files:
                flash('No file part')
                return redirect(request.url)
            file = request.files['file']
            # if user does not select file, browser also
            # submit an empty part without filename
            if file.filename == '':
                flash('No selected file')
                return redirect(request.url)
            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                return redirect(url_for('uploaded_file',
                                        filename=filename))
        return '''
        <!doctype html>
        <title>Upload new File</title>
        <h1>Upload new File</h1>
        <form method=post enctype=multipart/form-data>
          <input type=file name=file>
          <input type=submit value=Upload>
        </form>
        '''
    
    
    from flask import send_from_directory
    
    @app.route('/uploads/<filename>')
    def uploaded_file(filename):
        return send_from_directory(app.config['UPLOAD_FOLDER'],
                                   filename)
    
    那么 secure_filename() 函数到底是有什么用?有一条原 则是“永远不要信任用户输入”。这条原则同样适用于已上传文件的文件名。所有提 交的表单数据可能是伪造的,文件名也可以是危险的。此时要谨记:在把文件保存到 文件系统之前总是要使用这个函数对文件名进行安检。(上面的代码中有例子)
    

    改进上传

    如果上传的文件很小,那么会把它们储存在内 存中。否则就会把它们保存到一个临时的位置(通过 tempfile.gettempdir() 可以得到这个位置)
    
    可以通过设置配置的 MAX_CONTENT_LENGTH 来限制文 件尺寸:
        from flask import Flask, Request
    
    	app = Flask(__name__)
    	app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
       
    上面的代码会把尺寸限制为 16 M 。如果上传了大于这个尺寸的文件, Flask 会抛 出一个 RequestEntityTooLarge 异常。
    
    因为所有应用中上传文件的方案基本相同,因此可以使用 Flask-Uploads 扩展来 实现文件上传。这个扩展实现了完整的上传机制,还具有白名单功能、黑名单功能以 及其他功能。
    

    Cookies

    可以使用响应 对象 的 set_cookie 方法来设置 cookies
    #获取cookie
    from flask import request
    
    @app.route('/')
    def index():
        username = request.cookies.get('username')
        # use cookies.get(key) instead of cookies[key] to not get a
        # KeyError if the cookie is missing.
        
        
    #设置cookie
    from flask import make_response,render_template
    
    @app.route('/')
    def index():
        resp = make_response(render_template('index.html'))
        resp.set_cookie('username', 'the username')
        return resp
    
    -------------------------------------------------------------
    #延迟的请求回调
    
    比如在一个 before_request() 回调函数中,我们需要根据响应对象设置一 个 cookie 。
    
    例如可以尝试把应用逻辑移动到 after_request() 回调函数中。但是,有时候 这个方法让人不爽,或者让代码变得很丑陋
    
    下例在 before_request() 回调函数中在 cookie 中记住了当前用户的 语言:
    
    
    
    
    
    from flask import request, after_this_request
    
    @app.before_request
    def detect_user_language():
        language = request.cookies.get('user_lang')
    
        if language is None:
            language = guess_language_from_request()
    
            # when the response exists, set a cookie with the language
            @after_this_request
            def remember_language(response):
                response.set_cookie('user_lang', language)
                return response
    
        g.language = language
    
    

    重定向和错误

    使用 redirect() 函数可以重定向。使用 abort() 可以 更早退出请求,并返回错误代码:
        
    from flask import Flask, url_for, abort, redirect, request, render_template
    
    app = Flask(__name__)
    app.debug = False
    
    
    @app.route('/')
    def index():
    	return redirect(url_for('login'))
    
    
    @app.route('/login', methods=['GET'],endpoint='login')
    def func():
    	abort(500)
    
    
    if __name__ == '__main__':
    	app.run()
    -------------------------------------------------------------
    #当访问的路径不存在的时候会走这个函数  第三号的404 下面的404提示 如果不写会  显示200
    127.0.0.1 - - [20/Apr/2021 20:26:18] "GET /l HTTP/1.1" 404 -
    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('page_not_found.html'),404
    
    
    #还可以传递一个异常
    @app.errorhandler(ZeroDivisionError)
    def page_not_found(error):
        return '服务器正在微信请您稍等片刻,不要放弃我们啊',500
    
    

    关于响应

    如果返回值是一个字典,那么会调用 jsonify() 来产生一个响应。以下是转换的规则:
    
    1.如果视图返回的是一个响应对象,那么就直接返回它。
    
    2.如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
    
    3.如果返回的是一个字典,那么调用 jsonify 创建一个响应对象。
    
    4.如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。
    
    5.如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
    
    6.如果想要在视图内部掌控响应对象的结果,那么可以使用 make_response() 函数。
    
    
    如果想要在视图内部掌控响应对象的结果,那么可以使用 make_response() 函数。
    
    设想有如下视图:
    
    @app.errorhandler(404)
    def not_found(error):
        return render_template('error.html'), 404
    可以使用 make_response() 包裹返回表达式,获得响应对象,并对该对象 进行修改,然后再返回:
    
    @app.errorhandler(404)
    def not_found(error):
        resp = make_response(render_template('error.html'), 404)
        resp.headers['X-Something'] = 'A value'
        return resp
    
    

    JSON 格式的 API

    如果从视图 返回一个 dict ,那么它会被转换为一个 JSON 响应。
    
    还需要创建其他类型的 JSON 格式响应,可以使用 jsonify() 函数。该函数会序列化任何支持的 JSON 数据类型。 也可以研究研究 Flask 社区扩展,以支持更复杂的应用。
    
    @app.route("/users")
    def users_api():
    	return jsonify([i for i in range(0,100)])
    
    
    @app.route('/login', methods=['GET'], endpoint='login')
    def func():
    	return {
    		'gzs': '格式',
    		'user':'genzeshi',
    		'age':18
    			}
    
    
    
    
    

    会话

    除了请求对象之外还有一种称为 session 的对象,允许你在不同请求 之间储存信息
    
    这个对象相当于用密钥签名加密的 cookie ,即用户可以查看你的 cookie ,但是如果没有密钥就无法修改它
    
    使用会话之前你必须设置一个密钥。举例说明:
    
    from flask import Flask, session, redirect, url_for, request
    from markupsafe import escape
    
    app = Flask(__name__)
    
    # Set the secret key to some random bytes. Keep this really secret!
    app.secret_key = b'_5#y2L"F4Q8z
    xec]/'
    
    @app.route('/')
    def index():
        if 'username' in session:
            return 'Logged in as %s' % escape(session['username'])
        return 'You are not logged in'
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
    
    @app.route('/logout')
    def logout():
        # remove the username from the session if it's there
        session.pop('username', None)
        return redirect(url_for('index'))
    
    -------------------------------------
    from flask import Flask, escape, session, url_for, abort, redirect, request, render_template
    
    app = Flask(__name__)
    app.debug = False
    app.secret_key="3423rjfjoejf3j23rfwejfwj"
    
    @app.route('/login')
    def login():
    	if 'username' in session:
    		print(session.get('username'))
    		return 'Logged in as %s' % escape(session['username'])  #可以取值放到字符串中
    	return 'You are not logged in'
    
    
    @app.route('/')
    def index():
    	session['username'] = 'gzs'
    	return '存储完毕'
    
    
    if __name__ == '__main__':
    	app.run()
    
    

    消息闪现

    闪现系统的基 本工作方式是:在且只在下一个请求中访问上一个请求结束时记录的消息。一般我们 结合布局模板来使用闪现系统。注意,浏览器会限制 cookie 的大小,有时候网络服 务器也会。这样如果消息比会话 cookie 大的话,那么会导致消息闪现静默失败
    
    
    from flask import Flask, flash, redirect, render_template, 
         request, url_for
    
    app = Flask(__name__)
    app.secret_key = b'_5#y2L"F4Q8z
    xec]/'
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        error = None
        if request.method == 'POST':
            if request.form['username'] != 'admin' or 
               request.form['password'] != 'secret':
                error = 'Invalid credentials'
            else:
                flash('You were successfully logged in')
                return redirect(url_for('index'))
        return render_template('login.html', error=error)
    以下是实现闪现的 layout.html 模板:
    
    <!doctype html>
    <title>My Application</title>
    {% with messages = get_flashed_messages() %}
      {% if messages %}
        <ul class=flashes>
        {% for message in messages %}
          <li>{{ message }}</li>
        {% endfor %}
        </ul>
      {% endif %}
    {% endwith %}
    {% block body %}{% endblock %}
    以下是继承自 layout.html 的 index.html 模板:
    
    {% extends "layout.html" %}
    {% block body %}
      <h1>Overview</h1>
      <p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
    {% endblock %}
    以下是同样继承自 layout.html 的 login.html 模板:
    
    {% extends "layout.html" %}
    {% block body %}
      <h1>Login</h1>
      {% if error %}
        <p class=error><strong>Error:</strong> {{ error }}
      {% endif %}
      <form method=post>
        <dl>
          <dt>Username:
          <dd><input type=text name=username value="{{
              request.form.username }}">
          <dt>Password:
          <dd><input type=password name=password>
        </dl>
        <p><input type=submit value=Login>
      </form>
    {% endblock %}
    
    

    日志

    这时候就需要使用日志来记录这些不正常的东西了。自从 Flask 0.3 后就已经为你配置好 了一个日志工具。
    
    app.logger.debug('A value for debugging')
    app.logger.warning('A warning occurred (%d apples)', 42)
    app.logger.error('An error occurred')
    
    

    配置管理

    不管你使用何种方式载入配置,都可以使用 Flask 对象的 config 属性来操作配置的值
    
    
    ENV 和 DEBUG 配置值是特殊的,因为它们如果在应用设置完成之 后改变,那么可以会有不同的行为表现。为了重可靠的设置环境和调试, Flask 使 用环境变量。
    
    环境用于为 Flask 、扩展和其他程序(如 Sentry )指明 Flask 运行的情境是什么。 环境由 FLASK_ENV 环境变量控制,缺省值为 production 。
    
    把 FLASK_ENV 设置为 development 可以打开调试模式。 在调试模式下, flask run 会缺省使用交互调试器和重载器。如果需要脱离 环境,单独控制调试模式,请使用 FLASK_DEBUG 标示。
    
    #配置入门
    app = Flask(__name__)
    #可以像下面这样 书写配置	
    app.config['TESTING'] = True
    
    一次更新多个配置值可以使用 dict.update() 方法:
    app.config.update(
    	SECRET_KEY="3424fgrg",
    	DEBUG=True,
    	TESTING=True,
    )
    
    ----------------------------------------------------------------------------------------
    #内置配置变量
    ENV
    	缺省值: 'production'
        在生产环境中不要使用 development 。
    DEBUG
    	在生产环境中不要开启调试模式
        True
        False
    TESTING
    	你应当在自己的调试中开启本变量。
        False
        True
    SESSION_COOKIE_PATH
    	认可会话 cookie 的路径。如果没有设置本变量,那么路径为 APPLICATION_ROOT ,如果 APPLICATION_ROOT 也没有设置,那么会是 / 。
    
    	缺省值: None
        
    MAX_COOKIE_SIZE
    	当 cookie 头部大于本变量配置的字节数时发出警告。缺省值为 4093 。 更大的 cookie 会被浏览器悄悄地忽略。本变量设置为 0 时关闭警告。
    
    

    补充

    #(xss攻击)跨站脚本攻击是指在一个网站的环境中注入恶任意的 HTML (包括附带的 JavaScript )
    flask预防了xss攻击:所有从后端传到前段的html代码和js代码默认是不让浏览器识别的
    
    如果想让前段识别html或者是js代码可以使用 Markup 方法,代码写在这个函数里面
    
    发送上传的 HTML ,永远不要这么做,使用 Content-Disposition: attachment 头部来避免这个问题。
        
    虽然 Jinja2 可以通过转义 HTML 来保护你免受 XSS 问题,但是仍无法避免一种情况:属性注入的 XSS 。为了免受这 种攻击,必须确保在属性中使用 Jinja 表达式时,始终用单引号或双引号包裹:
    #例子 
    <input value="{{ value }}">
    这样做的目的是什么?
    这样做的目的是方式攻击者使用html+js代码进行对网站的攻击
    -------------------------------------------------------------
    
    有一类 XSS 问题 Jinja 的转义无法阻止。 a 标记的 href 属性可以包含 一个 javascript: URI 。如果没有正确保护,那么当点击它时浏览器将执行其代 码。
        
    #例子
    
    <a href="{{ value }}">click here</a>
    <a href="javascript:alert('unsafe');">click here</a>
    
    #攻击演示案例
    #html中的代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <script>
        function one(a,b) {
            alert(a+b)
        }
    
    </script>
    <img src="/static/1.png" alt="">
    <a href="javascript:onclick='one(1,2)'">点击</a>
    </html>
    
    
    #.py文件中的代码
    @app.route('/', methods=['GET'])
    def index():
    
    	return render_template('index.html')
    
    为了防止发生这种问题,需要设置 Content Security Policy (CSP) 响应头部。
    
    告诉浏览器哪里可以加载各种资源。这个头部应当尽可能使用,但是需要为网站定义 正确的政策。一个非常严格的政策是:
    
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    
    告诉浏览器把所有 HTTP 请求转化为 HTTPS ,以防止 man-in-the-middle (MITM) 攻击。
    
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    
    --------------------------------------------------------------------------------------------------------------------------
    跨站请求伪造( CSRF )
    那么如何预防这个问题呢?基本思路是:对于每个要求修改服务器内容的请求,应该 使用一次性令牌,并存储在 cookie 里, 并且 在发送表单数据的同时附上它。 在服务器再次接收数据之后,需要比较两个令牌,并确保它们相等。
    #这个预防测试flask没有做
    --------------------------------------------------------------------------------------------------------------------------
    json安全
    
    
    
    
  • 相关阅读:
    elasticsearch的基本用法
    JavaScript实现拖拽预览,AJAX小文件上传
    php四排序-选择排序
    php四排序-冒泡排序
    centos6.5编译安装lamp开发环境
    centos7.2 yum安装lamp环境
    chrome升级54以后,显示Adobe Flash Player 因过期而遭到阻止
    chrome45以后的版本安装lodop后,仍提示未安装解决
    APACHE重写去除入口文件index.php
    PHP之factory
  • 原文地址:https://www.cnblogs.com/happyyou123/p/14710048.html
Copyright © 2020-2023  润新知