# Python Flask_變量與請求 ###### tags: `python` `flask` `變量` `請求` flask內有四種上下文全域變量與四種請求 ## 上下文全域變量 * 程序上下文 * current_app * 當前程序 * g * 請求上下文 * request * 封裝了client端發出的http請求中的內容 * session * 用來保存請求之間的資 ### current_app ### g [官方說明](http://dormousehole.readthedocs.io/en/latest/api.html#flask.g) 處理請求作用時臨時儲存的對象,每次請求都會重新設置 :::info Just store on this whatever you want. For example a database connection or the user that is currently logged in. 什麼都可以保存,資料庫連線或是當前使用者資料 ::: ```python user = getattr(flask.g, 'user', None) user = flask.g.get('user', None) ``` :::info It’s now also possible to use the in operator on it to see if an attribute is defined and it yields all keys on iteration. 現在也可以使用in來確認是否存在,也可以透過迭代來操作 ::: ### request ### session ## 請求 如果有多個before_request的時候,只要中間有執行return的部份,後續就不再被執行。 * before_first_request * 註冊一個函數,在處理第一個請求之前執行 * before_request * 註冊一個函數,在每次請求之前執行 * after_request * 註冊一個函數,如果沒有未處理的異常拋出,在每次請求之後執行 * teardown_request * 註冊一個函數,如果有未處理的異常拋出,在每次請求之後執行 ### 請求範例 ```python from flask import Flask, g, request app = Flask(__name__) @app.before_request def before_request(): print('before request started') print(request.url) @app.before_request def before_request2(): print('before request started 2') print(request.url) g.name = "Test request" @app.after_request def after_request(response): print('after request finished') print(request.url) response.headers['key'] = 'value' return response @app.teardown_request def teardown_request(exception): print('teardown request') print(request.url) @app.route('/abc') def index(): return 'Hello, %s!' % g.name if __name__ == '__main__': app.run(debug=True) ``` 執行之後,它的執行順序是一路下來的。 但是如果針對第一個before_request做return的話,第二個before_request是不會執行的。 ```python http://127.0.0.1:5000/abc from flask import Flask, g, request app = Flask(__name__) @app.before_request def before_request(): print('before request started') print(request.url) return 'Break!' @app.before_request def before_request2(): print('before request started 2') print(request.url) g.name = "SampleApp" @app.after_request def after_request(response): print('after request finished') print(request.url) response.headers['key'] = 'value' return response @app.teardown_request def teardown_request(exception): print('teardown request') print(request.url) @app.route('/abc') def index(): return 'Hello, %s!' % g.name if __name__ == '__main__': app.run(debug=True) ``` ## 範例 ### 資料庫連線 ```python @app.before_request def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): # 取得g的db屬性 db = getattr(g, 'db', None) # 如果不是沒None的話,即關閉資料庫連線 if db is not None: db.close() ``` 透過flask裝飾器,設置在請求之前做資料庫連線的初始化。 所以設置了一個db屬性給g=connect_db(),並在最後做資料庫的連線關閉。 ```python @app.route('/') def show_entries(): cur = g.db.execute('select title, text from entries order by id desc') entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] return render_template('show_entries.html', entries=entries) ``` 在請求進來之後,就可以直接透過g.db來做資料庫的操作。 ```python @app.route('/add', methods=['POST']) def add_entry(): if not session.get('logged_in'): abort(401) g.db.execute('insert into entries (title, text) values (?, ?)', [request.form['title'], request.form['text']]) g.db.commit() flash('New entry was successfully posted') return redirect(url_for('show_entries')) ``` 判斷session若沒有登入訊息,就拋出401異常。 再透過flash送訊息之後重新定向到show_entries。 這邊也注意到一定要使用參數方式來處理,不用字串串接,否則會糟受到SQL injection攻擊