flask

2018-09-10  本文已影响0人  唯此

如何使用flask做vue的静态文件服务器?

原理: 让flask的静态文件与模板文件的根目录指向vue的静态文件地址.

from flask import Flask, render_template
def create_static_app(folder_path):
    app = Flask(__name__,
                static_folder = folder_path,
                template_folder= folder_path)
  
    @app.route('/', defaults={'rel_path': ''})
    @app.route('/<path:rel_path>') #解析url地址
    def path_serve(rel_path):
        if rel_path.split(".")[-1] in ["js","css","ico","ttf","woff","map"]:
            # 这些文件都从静态文夹里面获取
            return app.send_static_file(rel_path)
        elif not rel_path:
            return render_template("index.html")
        else:
            raise ("cannot deal with {}".format(rel_path))
    return app

if __name__ =="__main__":
    app = create_static_app(r"D:\gdrive\js_swz\searchtest\app\dist")
    app.run(host="192.168.1.9", port=9200, debug=True)

坑: flask的render_template方法会自动使用jinja2的语法来替换{{var}}.替换为空字符之后,vue再解析的时候就没有这些大括号了.最简单的处理方式是用app.send_static_file方法,这个方法不会去替换{{var}}

如何让flask自动热加载?

flask执行.run方法的时候,可以把debug选项改为True,这样可以热加载了.下图中我修改了main.py文件,然后flask感应到这个修改,执行了reload.


热加载示意

热更新的细节: 如果flask所以来的其他模块变化了,它自己会更新吗?

如何使用flask来处理post请求?

server端:

@app.route('/text', methods=['POST', 'GET'])
def convert_code():
    """
    使用reqeust.get_data()方法才能得到传进来的json
    :return:
    """
    code_text = request.get_data()
    json_data = json.loads(code_text.decode("utf-8"))
    text_content = json_data['text']

web端(jinjia模板):

let xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","http://{{host_ip}}:5000/text",true);
xmlhttp.send(JSON.stringify({text:t.value}));

flask 如何做文件服务器

核心是使用send_from_directory这个函数来获取指定路径上的文件.

from flask import Flask
from flask import request, render_template, make_response, jsonify,send_from_directory
from run_fishsite.instance import config as instance_cf
app = Flask(__name__)
@app.route("/phtml/<path:rel_path>")
def show_phtml(rel_path):
     return send_from_directory(instance_cf.phtml_dir, rel_path, as_attachment=False)  # as_attachment代码是下载还是打开

上传文件会略微复杂一下,可以使用原生的flask,也可以使用插件:https://pythonhosted.org/Flask-Uploads/

如何使用flask_login实现登录?

实现思路: 1. 实现登录认证. 2. 实现登录状态保持. 3. 同一个url根据登录状态来返回不同结果.
在flask_login库中登录状态是由User类的对象保持的.不同的session会有不同的User类对象. 这个插件封装了session中的设置.
flask_login本身的函数提供了必要的操作流程,包括login_user函数, logout_user函数.
log_manager是为了给flask_login提供扩展,可以配置不同的回调函数.包括user的id设置,未登录提示,current_user等.

#coding=utf-8
import flask
import flask_login
# ------------------------- 配置 -------------------------
secret_key =  'super secret string'  # flask要求任何与session相关的操作都需要密钥来确保安全性.
users = {'foo@bar.tld': {'password': 'secret'}}
app = flask.Flask(__name__)
app.secret_key = secret_key
# ------------------------- 定义User -------------------------
class User(flask_login.UserMixin): # 继承了基本的用户模型, User基本上就是一些属性的集合而已.
    pass

# ------------------------- 配置login_manager-------------------------
login_manager = flask_login.LoginManager()
login_manager.init_app(app) # 绑定到app中去

@login_manager.unauthorized_handler
def unauthorized_handler():
    return 'Unauthorized'


@login_manager.user_loader # 把user_loader挂载到login_manager上, 接受一个用户id作为参数,然后返回none或者user实例.
def user_loader(email):
    print("I am user_loader")
    if email not in users:
        return

    user = User() # 登录成功,新建一个User实例
    user.id = email
    return user


@login_manager.request_loader
def request_loader(request): # 不使用cookie的登录方法,可以直接从request对象中获取登录状态;flask-login会优先检查请求的cookie,当发现用户cookie不存在的时候才会根据request来验证
    print("I am request_loader")
    email = request.form.get('email')
    if email not in users:
        return

    user = User()
    user.id = email

    # DO NOT ever store passwords in plaintext and always compare password
    # hashes using constant-time comparison!
    user.is_authenticated = request.form['password'] == users[email]['password']  # 对比密码
    return user
# ------------------------- 使用flask_login-------------------------
@app.route('/login', methods=['GET', 'POST']) # 绑定登录页面
def login():
    if flask.request.method == 'GET':
        return '''
               <form action='login' method='POST'>
                <input type='text' name='email' id='email' placeholder='email'/>
                <input type='password' name='password' id='password' placeholder='password'/>
                <input type='submit' name='submit'/>
               </form>
               ''' # 返回输入框

    email = flask.request.form['email'] # 得到用户名(email)
    if flask.request.form['password'] == users[email]['password']: # 校验密码
        user = User()
        user.id = email
        flask_login.login_user(user) # 让用户登录进去, 此时会调用user_loader函数
        return flask.redirect(flask.url_for('protected')) # 登录之后的重定向到/protected, url_for是一个类似于相对路径的概念.

    return 'Bad login'# 密码不对


@app.route('/protected')
@flask_login.login_required # 需要登录
def protected():
    return 'Logged in as: ' + flask_login.current_user.id # flask_login.current_user能够识别出当前的userid, flask_login维护一个比login_manager更上层的状态

@app.route('/logout')
def logout():
    flask_login.logout_user()
    return 'Logged out'
# ------------------------- 运行app-------------------------
app.run(host="192.168.1.4", port=5004, debug= True)

如何处理报错?

@app.errorhandler(401)
def page_not_found(e):
    return Response('<p>Login failed</p>')

静态文件夹与路由的冲突

如果设置了静态文件夹,flask会自动根据url的地址来去静态文件中获取相应的文件.这样就忽略了用户自定义的路由函数.

app = Flask(__name__, static_folder=r"\\192.168.1.9\searchtest\app\static")
@app.route('/', defaults={'rel_path': ''})
@app.route('/path:rel_path>')
def path_serve(rel_path):
# mycode

此时所有get \static***这样的请求都会去静态文件夹中去找相应的文件.而忽略掉path_server

端口冲突的坑

某些端口能够启动flask,但是请求就是发不到flask上去.=>需要提前检查端口占用,被占用了就报错.

路由不识别根目录

@app.route("/dataeditor/<path:rel_path>")使用这样的路由会导致dataedtor这个根目录无响应.
坑: 默认的模板路径为templates而不是template

flask的自动多线程

使用了aiohttp这个异步请求的方法可以探知到flask自带多线程执行的.
案例1: 在view函数中,使用time.sleep(1), 然后连续发起两个请求,总耗时1.01秒左右.
案例2: 在view函数中使用全局变量,也不影响flask的时间消耗.

cout = [1]
    @app.route("/")
    def home():
        html_str = """
        cout[0] = cout[0] + 1
        print(cout[0])
        time.sleep(1)
        # return html_str
        return str(cout)

但是,如果调用了一个堵塞的io接口,那么会阻塞flask多线程的执行.例如tdx的交易接口.此时可以开启另一个tdx连接实例来降低延时.

坑: reqeust一般需要在view函数才可以引用.

flask多线程,可以使用 flask.copy_current_request_context这个装饰器,来获取当前的request中的信息.

参考:http://flask.pocoo.org/docs/1.0/api/#flask.copy_current_request_context

可以使用celery来实现后台进程,来不断地update数据.
http://flask.pocoo.org/docs/1.0/patterns/celery/

如何传递列表给flask

res = requests.get("http://192.168.1.4:5004/try_list", json=[1,3,4]) # 在flask 那边需要使用request.get_json来解析出列表来

坑: post请求被识别为get请求

如果post后面不加反斜杠的话,会被认为是get请求

requests.post(192.168.1.4:5000/ymarket, data=a_dict) # flask端会认为是get请求

坑: 在pycharm中启动flask

在pycharm中执行flask程序,会自动忽略app.run命令.因此需要在配置中配置好host与port


image.png

1

上一篇下一篇

猜你喜欢

热点阅读