在網頁中不可能只是按照設定好的 URL 去取得網頁頁面,在許多時候都需要帶入不同的參數去取得不同的資料,所以這篇就要來講一下如何接收傳入的參數。
如果需要將使用者傳入的值回傳給前端,那麼就需要對使用者輸入的值參數進行轉義,為什麼要這麼搞(費)剛(工)呢?因為不這樣處裡的話,就會產生 XSS 攻擊(Cross Site Scripting,跨站腳本攻擊。我也不太懂,用就對了,畢竟資安方面又是另外一個超級大坑了,改天有空再來填坑)。
那要怎麼處裡轉義呢?
from markupsafe import escape
# 記得實例化 Flask
@app.route('/xss-attack', methods=['GET'])
def XSS_attack():
return f'Hello {request.values["code"]}'
@app.route('/xss-safe', methods=['GET'])
def XSS_safe():
return f'Hello {escape(request.values["code"])}'
對,用 escape 把輸入的值包起來就可以了(記得先 import),接著就讓我們來看看有沒有使用轉義的差別。
首先先用這行網址,來試試沒有經過轉義會如何。http://localhost:5000/XSS-attack?code=<script>alert('XSS%20test')</script>
這樣就可能會造成 XSS 攻擊,現在還只是簡單的顯示個訊息,如果是將使用者的重要資料祕密的傳送給攻擊者,後果不堪設想。
反過來用這行網址,來試試經過轉義後會如何。http://localhost:5000/XSS-safe?code=<script>alert('XSS%20test')</script>
在這種情況下,即使攻擊者便無法透過這種方式攻擊。
如果現在要開發 API ,需要使用到 JSON 回傳,那又要如何回傳呢?
在 Python 中雖然有提供 JSON 模組,可以使用 json.dumps() 將資料轉換為 JSON 格式。但是,會出現一個問題,什麼問題呢,就來實驗一次,先將剛剛的 JSON 改成這樣:
import json
@app.route('/json-value', methods=['POST'])
def JSON_value():
if request.is_json:
data = request.get_json()
result = json.dumps(data)
else:
result = 'Not JSON Data'
return result
然後到 POSTMAN 中測試。
發現了嗎?沒有的話換另外一張。
看到了嗎,雖然以為回傳了 JSON 格式,但是因為標頭沒有寫回傳的是 JSON 格式,所以內容並不被當成 JSON 解析(雖然可以硬改標頭)。
為了解決這種情況,Flask 預設如果回傳的是 dict,就會轉換成 JSON 格式,並將改標頭改為 application/json
。現在將程式改成這樣:
@app.route('/json-value', methods=['POST'])
def JSON_value():
if request.is_json:
data = request.get_json()
result = data
else:
result = 'Not JSON Data'
return result
現在再回到 POSTMAN 測試一次。
看起來是有改成 JSON 格式了,來標頭確認看看。
好了,確實有改成使用 JSON 回傳了,可是如果是其他格式的資料需要用 JSON 回傳的話,還要費功夫將格式轉為 dict 才可以回傳也太麻煩了。所以 Flask 有提供 jsonify 的方式,可以把 Python 中其他格式的值轉換為 JSON 格式再回傳。像這樣:
@app.route('/json-value', methods=['POST'])
def JSON_value():
data = ['apple', 'banana', 'car']
return jsonify(data)
JSON 回傳資料大概就這樣。
雖然還沒介紹到前端頁面如何使用,不過我還是要稍微講一下如何傳值到前端頁面以及快速講一下如何渲染前端頁面。
Flask 要回傳(渲染?)一個頁面是使用 render_template()
這個 function,而 Flask 會自動到根目錄下的 templates
資料夾找頁面回傳,所以需要在跟目錄下建立資料夾並將頁面放在裡面。架構大概長這樣:
ithome
├── templates
│ └── index.html
├── app.py
├── configs.py
├── Pipfile
└── Pipfile.lock
檔案內容長這樣:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>template value</title>
</head>
<body>
<h1>Welcome {{ username }}</h1>
</body>
</html>
app.py
from flask import render_template
@app.route('/')
def index():
return 'Hello'
@app.route('/<user>')
def welcome(user):
# username 為 KEY 值,html 檔會用到;user 為 value,是要帶入的值
return render_template('index.html', username=user)
結果是這樣:
那麼就大概這樣,下一篇會好好的完整介紹一次前端頁面。
大家掰~掰~