Flask与Http

FlaskWeb工作流程

在这里插入图片描述

HTTP请求

  • 请求报文

    在这里插入图片描述

  • 使用request 的属性获取请求URL

    假设请求的URL 是http://127.0.0.1:5000/hello?name=Grey

    属性
    path/hello
    full_path/hello?name=gery
    host127.0.0.1:5000
    host_urlhttp://127.0.0.1:5000/
    base_urlhttp://127.0.0.1:5000/hello
    urlhttp://127.0.0.1:5000/hello?name=gery
    url_roothttp://127.0.0.1:5000/
  • request 对象常用的属性和方法

    属性、方法说明
    args
    blueprint当前蓝本名称
    cookies请求报文中cookies字典
    data
    endpoint与当前请求匹配的端点值
    files获取所有上传文件,使用字典形式获取. 调用save()方法传入路径保存文件
    form获取表单数据
    values结合了args和form属性的值
    get_data(cache=True, as_text=False, parse_from_data=False)获取消求中的数据, 默认读取为字节字符串( bytestring ), 将as 一text 设为True 返回值将是解码后的uni code 字符串
    get_json()
    headers返回首部字典,可以以字典形式操作
    is_json通过MIME 类型判断是否为JSON 数据,返回布尔值
    json包含解析后的json数据,内部调用get_json(),可通过字典方式获取键值
    method请求的HTTP方法
    referrer请求发起的源URL
    scheme请求的URL模式(http或https)
    user_agent用户代理,包含用户客户端类型、操作系统等信息
  • 请求钩子

    需要对请求进行预处理和后处理,可以使用flask的请求钩子.

    钩子说明
    before_first_request注册一个函数,在处理第一个请求前运行
    before_request注册一个函数,在处理每个请求前运行
    after_request注册一个函数,如果没有未处理的异常抛出,会在每个请求结束后运行
    teardown_request注册一个函数,即使有未处理的异常抛出,会在每个请求结束后运行. 如果发生异常,会传入异常对象作为参数到注册函数中.
    after_this_request在视图函数内注册一个函数,会在这个请求结束后运行
    • 钩子的常见应用场景

      • before_first_request:在应用程序中,运行程序前我们需要进行一些程序的初始化操作,比如创建数据库表、添加管理员用户等.
      • before_request:比如网站要记录用户最后在线的时间,可以通过用户最后发送的请求时间来实现. 为了避免在每个视图函数都添加更新在线时间的代码.
      • after_request:我们经常在视图函数中进行数据库操作,比如更新、插入等,之后需要提交更改到数据库中.

HTTP响应

  • 响应报文

    响应报文由协议版本、状态码、原因短语、响应首部和响应主体组成. 以localhost:5000/hello请求为例:
    在这里插入图片描述

  • 常见的http状态码

    在这里插入图片描述

  • 生成响应

    普通的响应只包含主体内容:

    @app.route('/hello')
    def hello():
      ...
      retuen '<h1>Hello, Flask!</h1>'
    

    默认的状态码为200,下面指定了不同的状态码:

    def hello():
      ...
      retuen '<h1>Hello, Flask!</h1>', 201
    

    重定向响应:默认情况下 redirect返回码为302.

    @app.route('/redirect')
    def redirect_302():
    	return redirect('http://www.baidu.com', code=301)
    	return redirect(url_for('greet'))  # 重定向到视图函数内部请求
    

    错误响应:

    @app.route('/404')
    def abort_404():
    	abort(404)    # abort之后的代码将不在执行
    
  • 响应格式

    在HTTP响应中,我们大多数情况使用HTML格式进行数据传输. 不同响应的数据格式需要设置不同的MIME类型,MIME类型在首部的Content-Type字段定义:

    MIME类型(又成为media type或content type)是一种用来标识文件类型的机制,可以让客户端区分不同的内容格式,执行不同的操作. 
    一般格式为"类型名/子类型名",比如"text/html"、"image/png"
    完整的MIME类型列表:https://www.iana.org/assignments/media-types/media-types.xhtml
    

    如果想要使用其他的MIME类型,可以通过Flask提供的make_response()方法生成响应对象,传入响应主体作为参数,然后使用响应对象的mimetype属性设置MIME类型,比如:

    @app.route('/foot')
    def foo():
    response = make_response("Hello, world!")    # 生成响应对象,参数为响应主体
    response.mimetype = "text/plain"   # 调用响应对象的mimetype属性进行设置MIME类型   纯文本
    return response
    

    常用的数据格式:

    数据格式说明
    text/plain纯文本
    text/htmlHTML(超文本标记语言)
    application/xmlXML(可扩展标记语言),XML中的标签用于定义数据,一般作为AJAX请求的响应格式或web api响应格式
    application/json较XML清量、简洁、容易阅读等优点

    flask中使用jsonify函数对json数据格式进行序列化输出,形成响应:

    @app.route('/foo')
    def foo():
    	return jsonify(name='Grey Li', gender='male')
    	return jsonify({name: 'Grey Li', gender: 'male'})
    	return jsonify(message="Error"!), 500
    
    以上两种形式返回值都是相同的,生成下面的JSON字符串:
    '{"name": "Grey Li", "gender": "male"}'
    另外,jsonify()函数默认生成200响应码,可以附加状态码自定义响应码.
    
  • Response类的常用属性和方法

    方法、属性说明
    headers一个Werkzeug的Headers对象,表示响应首部,可以像字典一样操作
    status状态码,文本类型
    status_code状态码,整型
    mimetypeMIME类型(仅包含内容类型部分)
    set_cookie()设置cookie

Cookie

HTTP是无状态协议. 对于一次请求、响应结束后,服务端不会记录任何请求相关信息.
但是对于某些Web程序,客户端的某些信息必须被记住,比如用户登陆状态,这样才可以根据用户的状态返回不同的响应.

Cookie技术通过在请求和响应报文中添加Cookie数据来保存客户端状态信息.

附注:Cookie指Web服务器为了存储某些数据(比如用户信息)而保存在浏览器上的小型文本. 浏览器会在一定时间内保存它,并在下一次向
同一个服务器发送请求时附带这些数据.
  • 响应添加Cookie

    在Flask中,一般使用Response类提供的set_cookie()方法. 要使用这个方法,我们需要先使用make_response()方法手动生成一个响应对象,传入响应主体作为参数. 这个响应对象默认实例化内置的Response类.

    set_cookie()方法参数:

    属性说明
    keycookie的键(名称)
    valuecookie的值
    max_agecookie被保存的时间数,单位为秒;默认在用户会话结束(即关闭浏览器)时过期
    expires具体的过期时间,一个datetime对象或Unix时间戳
    path限制cookie只在给定的路径可用,默认整个域名
    domain设置cookie可用的域名
    secure如果设置为True,只有通过HTTPS才可以使用
    httponly如果设置为True,禁止客户端JavaScript获取cookie

    示例:

    # 设置cookie
    @app.route('/set/<name>')
    def set_cookie(name):
    	response = make_response(redirect(url_for('say_hello')))
    	response.set_cookie('name', name)
    	return response
    

    浏览器第一次请求,可以看到响应对象已经携带set_cookie字段了,path为/
    浏览器第二次请求,请求对象携带cookie字段
    在这里插入图片描述
    在这里插入图片描述

Session: 安全的cookie

Cookie是不安全的,可以通过对Cookie内容进行修改获得对网站的权限,冒充别人账户. Flask提供session对象来将Cooke加密存储.

附注:在编程中,session指用户会话,即服务器和客户端/浏览器间建立的交互活动. 
在flask中,session对象用来加密cookie. 默认情况下,他会把数据存储在浏览器上一个名为session的cookie里.
  • 实现session步骤

    a) 设置程序密钥
    app.secret_key = 'Lsjsjljslslj'
    
    OR
    
    密钥写进.env文件或环境变量,使用os.getenv()获取
    app.secret_key = os.getenv('SECRET_KEY', 'secret string')
    
    b) 模拟用户登陆
    示例只是简单的演示,实际登陆,我们需要提供登陆表单,验证账号和密码正确性. session可以像字典一样操作,我们向session中添加一个logged true,表示已经认证.
    session是采用密钥加密发送到客户端的,因此session是不能被修改的,除非用户知道密钥.
    @app.route('/login')
    def login():
    	session['logged_in'] = True  # 写入session
    	return redirect(url_for('say_hello'))
    

Flask上下文

Flask有两种上下文:程序上下文和请求上下文.

  • 上下文全局变量

    Flask通过本地线程(thread local)技术将请求对象在特定的线程和请求中全局可访问.

    Flask提供了四个上下文全局变量,访问上下文环境中的信息:

    变量名上下文类别说明
    current_app程序上下文指向处理请求的当前程序实例对象
    g程序上下文替代python的全局变量用户,确保仅在当前请求可用. 用于存储全局数据,每次请求都会重设
    request请求上下文封装客户端发出的请求报文数据
    session请求上下文会话认证
    • current_app

      既然有了程序实例app对象,为什么还需要current_app对象. 因为程序有可能会出现多个程序实例(app),为了获取对应的当前程序实例,而不是固定的某一个程序实例,需要使用current_app变量.
    • g

      因为g存储在程序上下文中,而程序上下文随着每一个请求进入而激活,随着每一个请求的处理完毕而销毁,每次请求都会重设这个值.
      通常使用g结合请求钩子来保存每个请求处理前需要的全局变量,比如当前登入的用户对象、数据库连接等.
      @app.before_request
      def get_name():
      	g.name = request.args.get('name')
      
      设置这个函数后,在其他视图函数中可以直接使用g.name来获取对应的值. g也支持类似字典的get()、pop()、一级setdefault()方法.
  • 激活上下文

    Flask会在下面情况激活程序上下文:

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

    当请求进入时,flask自动激活请求上下文,这是我们可以使用request、session变量. 另外,当请求上下文被激活时,程序上下文也会被激活. 当请求处理完毕后,请求上下文和程序上下文自动销毁. 因此,在请求处理阶段,两者拥有相同的生命周期.

    提示:url_for()、jsonify()也依赖上下文,因此只能在视图函数使用. url_for()依赖请求上下文,
    jsonify()内部调用了current_app变量.
    
    • 手动激活上下文环境

      • 激活程序上下文
        from app import app
        from flask import current_app
        
        with app.app_context():
        	... current_app.name
        
      • 激活请求上下文
        from app import app
        from flask import request
        
        with app.test_request_context('/hello'):
        	... request.method
        

HTTP进阶

  • 重定向到上一个页面

    # 重定向回上一个页面
    @app.route('/foo')
    def foo():
    	return '<h1>Foo Page.</h1><a href="%s">Do something and redirect</a>' % url_for('do_something',
                                                                                    next=request.full_path)
    
    
    @app.route('/bar')
    def bar():
    	return '<h1>Bar Page.</h1><a href="%s">Do something and redirect</a>' % url_for('do_something',
                                                                                    next="http://www.baiud.com")
    
    
    # 先找next,再找referrer,最后重定向到hello页面
    def redirect_back(default='say_hello', **kwargs):
    	for target in request.args.get('next'), request.referrer:
       		# 如果target为真 继续往下走
        	if not target:
            	continue
        	if is_safe_url(target):
            	return redirect(target)        # 内部重定向写相对路径
        	return redirect(url_for(default, **kwargs))
    
    
    # 验证是否存在next重定向
    def is_safe_url(target):
    	ref_url = urlparse(request.host_url)
    	# 此处target=request.args.get('next') 也就是next跟的下一个页面
    	# 注意urljoin用法, 此处target会覆盖request.host_url,因为target=http://www.baidu.com
    	# 要是一个相对路径的话,那么会进行url拼接
    	test_url = urlparse(urljoin(request.host_url, target))
    	return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
    
    
    @app.route('/do_something')
    def do_something():
    	# 注意url_for用法 /hello?next=222
    	print(url_for('say_hello', next=222))
    	return redirect_back()
    
  • AJAX

    • 认识AJAX

      不重载页面的情况下和服务器进行数据交换. 加上JavaScript 和DOM(Document Object Model , 文档对象模型),我们就可以在接收到响应数据后局部更新页面.

    • JQuery发送AJAX请求

      附注1:AJAX配置参数
      附注2:JQuery和AJAX相关的方法

  • 返回局部数据

    处于处理AJAX请求的视图函数来说,我们不会返回完整的HTML响应,一般会返回局部数据,常见的三种类型如下:

    • 纯文本或局部HTML模板

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

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

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

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

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

    • 空值

      有些时候,程序中某些接受AJAX请求的视图并不需要返回数据给客户端,比如用来删除文章的视图. 这时我们可以直接返回空值,并将状态码指定为204(表示无内容),比如:

      @app.route('/post/delete/<int:post_id>', methods=['DELETE'])
      def delete_post(post_id):
      	...
      	return '', 204
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值