《FLASK实战》-P54-P67 上下文和HTTP进阶

Flask 上下文

  • 程序上下文(application context):存储了程序运行所必须的信息
  • 请求上下文(Request context):当客户端发来请求时,请求上下文抱哈了请求的各种信息,比如请求的URL,请求的HTTP方法。

上下文全局变量

每一视图函数需要上下文信息,如果将请求报文封装在Request中,得将它作为参数传入视图函数,这将导致大量的重复,增加视图函数的复杂度。

如果不传入这个参数,那么直接将Flask中导入一个全局的request对象,然后在视图函数中调用Request的属性获取数据。Flask会在每个请求后自动激活当前的上下文,在请求结束后,销毁这个临时的请求上下文。

flask提供了4个上下文全局变量:

变量名上下文类别说明
current_app程序上下文指向处理请求的当前程序实例
g程序上下文替代Python的全局变量用法,确保仅在当前请求中可用。用于存储全局数据,每次请求都会重设
request请求上下文封装客户端发出的请求报文数据
session请求上下文用于记住请求之间的数据,通过签名的Cookie实现
g 存储在程序上下文,随着每一个请求的进入而激活,随着每一个请求的处理完毕而销毁,所以每次请求都会重设这个值。通常结合请求钩子来保存每个请求处理所需要的全局变量。
from flask import g
@app.before_request
def get_name():
	g,name = request.args.get('name')

激活上下文

Flask自动激活上下文的情况:

  • 使用flask run命令启动程序时
  • 使用旧的app.run()启动程序时
  • 执行使用@app.cli.command()装饰器注册的flask命令时
  • 使用flask shell命令启动Python Shell时

激活后,我们可以使用 requestsession变量。当请求上下文被激活后,程序上下文也自动被激活,当请求处理完毕,这两个上下文自动被 销毁

此外,url_for()和jsonify()也依赖上下文。

除了自动激活上下文,也可以手动激活:

from app import app
from flask import current_app
with app.app_context():
	current_app.name
from app import app
from flask import current_app
app_ctx = app.app_context()
app_ctx.push()
current_app.name

‘app’

from app import app
from flask import current_app
with app.test_request_context('/hello'):
    request.method

‘GET’

同样地,pop()和push()

上下文钩子

flask为上下文提供了一个teardown_appcontext钩子,使用它注册的回调函数会在程序上下文被销毁时调用,而且通常也会在请求上下文被销毁时调用。例如,请求结束后销毁数据库连接:

@app.teardown_appcontext
def teardown_db:
    ...
    db.close()

HTTP进阶实践

重定向回上一个页面

在前面,我们使用**redirect()**函数生成重定向响应。

这个是直接跳转。另一种复杂情况是用户单击某个需要登录才能访问的连接,这时程序会重定向到登录页面,当用户登录后,合理的行为是重定向到用户登录前浏览的页面,以便用户执行未完成的操作,而不是直接重定向到主页。

1. 获取上一个页面的URL

有两种方法

  1. HTTP referer

    在flask中,用request.referrer获取

    避免request.referrer为空,需要添加备选项

    return redict(request.referrer of url_for('hello'))
    
  2. 查询参数

    在URL中手动加入包含当前页面URL的查询参数,一般名称为next

    next=request.full_path

    使用request.full_path获取当前页面的完整路径

    return redict(request.args.get('next',url_for('hello')))
    

可以将这两种方法搭配起来使用,首先尝试获取next,如果为空继续获取referrer:

def redict_back(default='hello',**kwargs):
    for target in request.args.get('next'),request.referrer:
        if target:
            return redict(target)
    return redict(url_for(default,**kwargs))

2. 对URL进行安全验证

referer和url容易被篡改,如果不验证,会形成开放重定向(Open Redirect)漏洞。

验证安全性:

from urllib.parse import urlparse,urljoin
from flask import request
def is_safe_url(target):
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url,target))
    return test_uil.scheme in ('http','https') and ref_url.netloc ==test_url.netloc

request.host_url获取程序内的注记URL

urljoin()将目标URL转换为绝对URL

**urlparse()**解析两个URL,最后对目标URL的URL模式和主机地址进行验证

使用AJAX技术发送异步请求

传统Web应用:

首先,频繁更新页面会牺牲性能,浪费服务器资源,同时降低用户体验。

此外,页面重新加载后才能显示结果很不合理。

1. 认识AJAX

指异步JavaScript和XML,是一系列技术的组合体。

AJAX基于XMLHttpRequest让我们可以在不重载页面的情况下和服务器进行数据交换。加上JavaScript和DOM,就可以在接收到响应数据后局部更新页面。XML指的是数据的交互格式,也可以是纯文本或HTML、JSON。XMLHttpRequest不仅支持HTTP协议,还支持FILE和FTP协议。

2. 使用jQuery发送AJAX请求

jQuery是流行的JavaScript库。对于AJAX,它提供了多个相关方法,另外它处理了不同浏览器的AJAX兼容问题。

参数参数值型及默认值说明
url字符串;默认为当前地址请求的地址
type字符串;默认‘Get’请求的方式,比如GET、POST、DELETE
data字符串;无默认发送到服务器的数据。会被jQuery自动转为查询字符串
dataType字符串;默认由jQuery自动判断可用:xml,html,script,json,jsonp,text
contentType字符串;默认‘application/x-www-form-rulencoded;charset=UTF-8’发送请求时使用的内容类型,即请求首部的Content-Type字段内容
complete函数;无默认请求完成的回调函数
sucess函数;无默认请求成功的回调函数
error函数;无默认请求失败后的回调函数

返回‘局部数据’

对于处理AJAX的视图函数,一般会返回局部数据,常见的三种类型如下:

  1. 纯文本或局部HTML模板

    纯文本可以在JavaScript中替换页面的文本值,而局部HTML这可以直接插入页面,比如返回评论列表:

    @app.route('/comments/<int:post_id>')
    def get_comments(post_id):
        ...
        return render_template('comments.html')
    
  2. JSON数据

    JSON数据可以在JavaScript中直接操作

    @app.route('/profile/<int:user_id>')
    def get_profile(userr_id):
    	...
        return jsonify(username=username, bio=bio)
    

    在jQuery中的ajax()方法的success回调中,响应主体的JSON字符串会被解析为JSON对象,我们可以直接获取并进行操作。

  3. 空值

    不需要返回数据给客户端,比如删除文章的视图

    return '', 204
    
  4. 异步加载长文章示例

    @app.route('/post')
    def show_post():
        post_body = generate_lorem_ipsum(n=2)
        return '''
    <h1>A very long post</h1>
    <div class="body">%s</div>
    <button id="load">Load More</button>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
    $(function() {
        $('#load').click(function() {
            $.ajax({
                url: '/more',
                type: 'get',
                success: function(data){
                    $('.body').append(data);
                }
            })
        })
    })
    </script>''' % post_body
    
    
    @app.route('/more')
    def load_post():
        return generate_lorem_ipsum(n=1)
    

HTTP服务器端推送

不管什么请求方式,服务器始终处于被动的应答状态,只有客户端发出请求,服务器端才会返回响应。

某些情况,需要服务器端主动推送(server push)。例如聊天室某个用户发送消息,服务器将消息推送给所有用户。比如社交网站在导航栏实时显示新提醒和私信数量,用户的在线状态更新,多人游戏,文档协作等。

实现吴福气段推送的一系列技术被合称为 HTTP Server Push。有传统轮询、长轮询、Server-Sent-Events(SSE)

除了这些推送技术还有WebSocket协议。

Web安全防范

1. 注入攻击

有系统命令注入、SQL注入、NoSQL注入、ORM注入。重点介绍SQL注入:

  1. 攻击原理

    编写SQL语句时,直接将用户传入的数据作为参数使用字符串拼接的方式插入到SQL查询。

  2. 攻击示例

    @app.route('/students')
    def boddy_tabele():
        password = request.args.get('password')
        cur = db.execute("SELECT * FROM students WHERE password='%S';" &password)
        resuts = cur.fetchall()
        return results
    

    如果参数值为‘or 1=1 --’会把表中的所有结果返回。

    如果参数值为‘; drop table users; --’会把删除表中所有记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值