Flask学习记录
记录服务框架的学习
参考官方教程 Flask框架学习
Flask是什么?
Flask是一个基于Python的微型web框架,它能够快速搭建一个网页或web服务。
目录结构
Flask的目录结构大致如下:
flask-demo/
  ├ run.py           # 应用启动程序
  ├ config.py        # 环境配置
  ├ requirements.txt # 列出应用程序依赖的所有Python包
  ├ tests/           # 测试代码包
  │   ├ __init__.py 
  │   └ test_*.py    # 测试用例
  └ myapp/
      ├ admin/       # 蓝图目录
      ├ static/
      │   ├ css/     # css文件目录
      │   ├ img/     # 图片文件目录
      │   └ js/      # js文件目录
      ├ templates/   # 模板文件目录
      ├ __init__.py    
      ├ forms.py     # 存放所有表单,如果多,将其变为一个包
      ├ models.py    # 存放所有数据模型,如果多,将其变为一个包
      └ views.py     # 存放所有视图函数,如果多,将其变为一个包快速上手
安装直接pip安装就行,就省略了
HelloWorld
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
    return 'Hello World'
if __name__ == '__main__':
    app.debug = True # 设置调试模式,生产模式的时候要关掉debug
    app.run()python运行后访问localhost:5000即可看到
- Flask类是主要的框架,__name__使得Flask能够找到template等其他东西,让flask.helpers.get_root_path函数通过传入这个名字确定程序的根目录,以便获得静态文件和模板文件的目录。
- 在flask中,定义路由最简便的方式,是使用程序实例的app.route装饰器,把装饰的函数注册为路由
绑定参数
除了字符串这种静态路由,route也可以绑定参数的路由通过"
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return f'User {escape(username)}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return f'Post {post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return f'Subpath {escape(subpath)}'- 这里的path类指带斜杠的字符串
url_for
url_for可以通过反向路径构造指定函数的路由
from flask import url_for
@app.route('/')
def index():
    return 'index'
@app.route('/login')
def login():
    return 'login'
@app.route('/user/<username>')
def profile(username):
    return f'{username}\'s profile'
with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))
'''
Outputs:
/
/login
/login?next=/
/user/John%20Doe
'''静态文件
静态文件在flask中存储在static文件夹下,使用url_for()能够构造静态文件的URL
url_for('static', filename='style.css')HTTP方法
HTTP常用的请求方法
- get 用来获取资源
- post 传输实体获得回复
- put 传输文件
- head 获取首部
- delete等等
在默认情况下,一个路由只会回应GET请求,但是可以使用route()装饰器中的method参数来处理不同的HTTP方法。
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()如果GET被设定为可接受,则flask自动的会支持head方法
加载模板
Flask配置了Jinja2模板引擎,我们可以使用它来绘制界面。 通过render_template():
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)这个hello.html存储在当前目录中的templates文件夹 Jinja2的模板提供了许多强大功能,例如:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}在模板中同样可以使用各种各样的函数并访问变量。
模板继承
模板继承对于需要频繁重用的网页部件例如导航栏等十分有用。 我们可以创建一个父类模板layout.html:
<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
    {% block body %}
    {% endblock %}
</div>然后再创建一个子类,并继承layout.html:
{%  extends  "layout.html"  %}
{%  block  body  %}
{%  if  name  %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{%  endif  %}
{%  endblock  %}此时子类的内容会被加载到block body中,父类的其他内容依然会被显示。
HTML自动转义
我们有时不希望提交的表单中含有的HTML标签被自动转移,因此可以使用Markup类:
from  flask import  Flask,  Markup
app  =  Flask(__name__)
@app.route('/')
def  index():
    return  Markup('<div>Hello %s</div>')  %  '<em>Flask</em>'
format应当放在Markup外面
Markup还有许多方法
request
request对象用来处理请求
@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)常用方法
- request.method可以用来获取当前的HTTP方法
- request.form可以得到提交表单的内容,如果form中没有需要的键值就会报KeyError,并出现400错误页面
- request.args.get可以获取URL中的参数
文件上传
传输文件需要在HTML提交表单中设置enctype="multipart/form-data"
上传的文件可以通过request.file获得,是一个python的file对象,并拥有一个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')
    ...Cookies
可以使用request.cookies访问cookies,使用set_cookie()设置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.
from flask import make_response
@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return respmake_response()可以生成一个response对象用来对网页返回的内容进行更多修改(例如cookies)
每个经过app装饰器修饰的函数会将return的内容自动转换为response对象。
重导向与报错
redirect()可以改变访问的路由,abort(int)可以进行报错,实际开发中并不经常使用abort,而是进行异常的抛出或捕获。
from flask import abort, redirect, url_for
@app.route('/')
def index():
    return redirect(url_for('login'))
@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()使用errorhandler()装饰器来装饰报错页面
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404此外,errorhandler也可以自定义异常类型
API
常用的API接口调用会返回JSON,使用Flask返回Json十分简单,返回字典格式即可
@app.route("/me")
def me_api():
    user = get_current_user()
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }对于返回不是字典的api,也可以通过jsonify()来序列化数据
from flask import jsonify
@app.route("/users")
def users_api():
    users = get_all_users()
    return jsonify([user.to_json() for user in users])Session
session会话,和request一样是flask中的全局对象,用来保存当前请求的一些状态,用来再之后与同一用户的通信中共享信息。session将信息加密并保存在cookies中。
from flask import session
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {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'))日志
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')SocketIO
WebSocket - 基于 Python 的主流实现方式总结 websocket可以用来进行服务器与客户端之间的持续双工通信 flask作为服务端可以使用flask-socketio库 注意安装需要同时安装python-socketio、python-engineio等等,且版本需要匹配
异步任务
有时需要处理一些耗时的任务,那么就可以通过多线程、协程等异步的进行。 Python Flask后端异步处理
python自带的多线程
我们可以使用ThreadPoolExecutor进行
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(4)
@app.route('/')
def someTask():
    executor.submit(longTimeTask, *args)Celery
python自带的库虽然很方便,但是有时我们需要随时获取任务的各种结果,对任务进行更加复杂的操作。 我们还可以使用Celery进行异步分布式任务队列服务,它支持定时任务、即时任务等等。
将localhost映射到公网
通过使用 ngrok ,我们可以把本地的端口映射到公网,在别的地方都能访问,方便我们进行测试 打开下载的exe,输入
> ngrok http 5000就可以将端口5000映射到公网,具体的网址会显示在cmd中。
测试工具
API
待补充……