Files
Obsidian-Main/05. 資料收集/04. Programming/Flask.md

206 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 執行
### Linux
```bash
export FLASK_APP=RobotRunAutoServer.py
export FLASK_DEBUG=1
flask run --reload
```
### Windows
```
set FLASK_APP=RobotRunAutoServer.py
set FLASK_DEBUG=1
flask run --reload
```
## 路由
```python
@app.route('/')
def index():
return 'Index Page'
@app.route('/query/<SOMETHING>', methods=['GET', 'POST'])
def query(SOMETHING):
return 'Hello, {}!'.format(SOMETHING)
```
## 靜態檔案
```
url_for('static', filename='style.css')
```
## 模板
```python
from flask import render_template
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
```
依照 Flask 的慣例,它會在 templates 資料夾內去找模板檔,而 templates 資料夾的位置會根據這支 web app 是模組或是套件而有所不同。
如果是模組:
```
/application.py
/templates
/hello.html
```
如果是套件:
```
/application
/__init__.py
/templates
/hello.html
```
## 存取 request 資料
客戶端傳來的資料都放在 `request` 這個全域變數內web app 利用 `request` 提供的資訊與客戶端互動。
客戶端的 HTTP 方法存放在 `request``method` 屬性內,而 form 就存放在 `request``form` 屬性內。示例:
```Python
from flask import request
from flask import render_template
@app.route('/login', methods=['GET', 'POST'])
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)
```
如果 `form` 屬性不存在,伺服器端會引發 `KeyError` 錯誤,客戶端則會收到 HTTP 400 錯誤。
如果是要取用 `URL` 參數,則使用 `args` 屬性內的 `get()` 方法:
```Python
searchword = request.args.get('key', '')
```
如果是要取用客戶端上傳的檔案,先確定在前端 HTML 表單的設定正確的屬性 `enctype="multipart/form-data"`,瀏覽器才會正確的把檔案上傳。調用 `request``files` 屬性就可以調用到檔案:
```Python
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')
```
`files` 本身是個 dictionary所以必須呼叫它裡面的 key 才會接到真正的檔案,這個 `file` 物件的行為就像標準的 Python `file` 物件,但它有個 `save()` 方法讓我們把檔案存到自己想要的路徑。
如果想要沿用客戶端上傳的檔名,則調用 `filename` 屬性,但由於檔名的不可預知,可能會有安全風險,最好用 Werkzeug 的 `secure_filename()` 方法過濾掉:
```Python
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
```
如果是要讀取 cookie 就調用 `request` 物件的 `cookies` 屬性,如果是要設置 cookie 就用 `response` 物件的 `set_cookie()` 方法。
`request``cookies` 也是一個 dictionary裡面放了所有可以調用的 cookie。
但是如果想使用 session 的話Flask 有提供更完善的 session 機制可以利用,不要手工用 cookie 來管理 session。
讀取 cookie 的範例:
```Python
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.
```
存入 cookie 的範例:
```Python
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
```
## 重導頁面與錯誤頁面
要重導使用 `redirect()` 方法,要中斷並報錯用 `abort()` 方法:
```Python
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()
```
如果不想使用 Flask 預設的陽春錯誤頁,則利用 `errorhandler()` 修飾子來做客製:
```Python
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
```
注意到 `return` 那行最後面的 `404`,雖然上面的修飾子已經是 404`return` 後面還是要加 `404` Flask 才認得這是 404 錯誤頁。
## Session
Session 用來紀錄、辨識用戶的活動,實現方式是加密過的 cookie。
得先設置密鑰才能使用 session
```Python
from flask import Flask, session, redirect, url_for, escape, request
from werkzeug.utils import secure_filename
app = Flask(__name__)
# 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 {escape(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'))
```
前面模板的章節有說過,模板引擎會幫我們把表單的 HTML 過濾掉,而在這裡沒有使用模板引擎,所以手動調用了 `escape()` 方法來濾掉 HTML 碼。
最後附註一點,瀏覽器可能會限制單一 cookie 容量,如果發現某個值應該要有卻調用不出來的話,想想看是不是超過 cookie 的容量上限了。
## Log 紀錄
Flask app 物件有使用 Python 內建的 `logger` 模組,可以簡單調用:
```Python
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
```