網頁後端 Flask
講師介紹:

成電CKCSC 38th - 教學 + 網管

講師介紹:
興趣?(除了扣丁之外)






講師介紹:
學術力:
競程、演算法、網頁設計、python
APCS 中高級 5+4、北市賽三等獎 第17名
(對是打競程的我不知道為什麼我來講後端了 :P)
歐對 我打字很快算學術力嗎 :P

後端
是什麼可以吃嗎?

什麼是後端?
就是在伺服器處理前端不能處理的東西
例如:密碼登入之類的東西


什麼是後端?

前端:你看的到的

後端:伺服器處理的
什麼是後端?
沒有後端的網站 -> 靜態網站
有後端的網站 -> 動態網站
處理後端的程式語言有非常多種:
像是 php, javascript, python 之類的
而我們今天要教的是python的Flask函式庫,專門來處理網頁後端的
什麼是後端?
來看看一個有後端的網站吧
100%不是我寫的扣
Flask
下載

錐形瓶?

python web framework
flask

Flask
Python
(就看你現在這台電腦有沒有載吧)
1. 下載python:網站

2. 在vscode確認有沒有選對的版本:
Flask
如何使用Flask
1. 先在vscode建立一個簡單的前端網站
(我相信你們昨天才學過 還記得怎麼作)


Flask
如何使用Flask
2. 下載vscode的python extension

Flask
如何使用Flask
3. 下載Flask package


pip install Flask開啟terminal
打入這個指令
Flask
小插入 先解釋一個檔案再電腦中的途徑
那要怎麼從一個檔案走到另一個檔案呢?
(讓html知道我們的檔案在哪裡)

假設我們要從index.html走到funny.png:
一些要知道的小知識:
"." - 現在這個資料夾
"/" - 要走的途徑
".." - 返回上一層資料夾
所以就會是:(拿昨天學得的前端來當例子)
<link href="./images/funny.png" rel="icon">Flask
如何使用Flask
4. 建立一個python檔案 (.py)
5. 檔案結構(Flask才能運作)

html 放 ./templates
css 放 ./static/css
js 放 ./static/js
圖片放 ./static/images
Flask
如何使用Flask
請大家先做到這步
(接下來就要叫大家打扣了)
(也可以先寫個前端idk)
Python
基礎語法
Python
(這裡預設你已經有學過一些程式語言)
變數
a = 10 #整數
b = 20.2 #小數
c = 'string' #字串
d = [10, 30, 40] #陣列
# 對不需用跟他說是什麼類型Python
if
a = 10
b = 20
if a == 15:
print("a is 15")
elif b == 20:
print("b is 20")
else:
print("neither is true")
#對 python 不用小括號
#對 python 不用分號
#對 python 不用大括號 (不下放btw)Python
while
a = 10
while a < 20:
print(a)
a += 1
# 應該蠻直觀的Python
for
# 重複10次
for i in range(10): #i 會是 0~9
print(i)
for i in range (2, 19, 2): #如果翻譯成c++或js就會是 for (int i=2; i<19; i+=2)
print(i)
arr = ["apple", "banana", "tomato"]
for i in arr: #i 會是 arr 中的元素
print(i)
Python
function
def add(a, b):
ans = a+b
return ans
print(add(1, 2))
#python宣告函式的方法是def
#剩下就和其他語言差不多了Python
f string
fruit = "apple"
print(f'My favorite fruit is {fruit}')
#可以在字串中加入變數 很好用Flask
語法
Flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True,port=5000)基礎模版
除了3-5行之外
大致上都是複製貼上 可以先不用理解
(只要知道他導入Flask
然後可以讓網站跑就好了)
Flask
基礎模版
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True,port=5000)Flask
@app.route("/a")
def helloa():
return "Hello, World! aaa"這個函式跑得網站:
http://127.0.0.1:5000/a
from flask import render_template
@app.route("/a")
def hellob():
name = "John"
return render_template("index.html", name=name)<h1>Hello {{ name }}!</h1>html檔案 (等一下會介紹 不急)
@app.route('/add/<int:a>/<int:b>')
def add(a, b):
return f'{a} + {b} = {a+b}'http://127.0.0.1:8080/add/3/4

如何顯示前端
Flask

Flask
小補充:
如何將使用者導入其他網址
from flask import redirect, url_for
@app.route("/")
def main():
return redirect(url_for("other_route"))
#將使用者導到 /other_route 這個網址Flask
實作一
/random/<int:min>/<int:max>
這個路由會在 min 和 max 之間產生一個隨機數並回傳,例如:
/random/1/100 → 隨機數:67
- 提示:
import random
使用 random.randint(min, max)

Flask
from flask import Flask
import random
app = Flask(__name__)
@app.route('/random/<int:a>/<int:b>')
def main(a, b):
return f'random number is {random.randint(a, b)}'
if __name__ == '__main__':
app.run(debug=True,port=5000)解答:
Jinja
Jinja

能把在後端把一些資訊傳給前端的東西
Jinja
{{ 兩個大括號是變數 }}
{% 執行jinja2內置函數 %}
{# 註解說明 #}語法:
在flask傳出變數,並顯示html:
from flask import render_template
name = "World"
return render_template("index.html", name=name)Jinja
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def hello():
name = "World"
return render_template("index.html", name=name)
if __name__ == '__main__':
app.run(debug=True,port=5000)<h1>Hello {{ name }}!</h1>html檔案
Flask

Jinja
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def main():
name = "admin"
return render_template("index.html", name=name)
if __name__ == '__main__':
app.run(debug=True,port=5000) {% if name == "admin" %}
<h1>Logged in, Hello {{ name }}!</h1>
{% else %}
<h1>Not Logged in, Try again!</h1>
{%endif%}Flask
html檔案
Jinja
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def main():
items = ["apples", "bananas", "tomatoes"]
return render_template("index.html", items=items)
if __name__ == '__main__':
app.run(debug=True,port=5000) {% for item in items %}
<li> {{ item }} </li>
{% endfor %}Flask
html檔案
Jinja
小補充:
如何在html導入css, js
(可以直接用url_for)
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/main.js') }}"></script>Jinja
實作二
後端有一個數字陣列 arr
在前端迭代 arr
如果是偶數就用 <li> render那個數字出來
Jinja
解答:
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def main():
arr = [1, 2, 5, 7, 3, 8, 10]
return render_template(("index.html"), arr = arr)
if __name__ == '__main__':
app.run(debug=True,port=5000) {% for i in arr %}
{% if i%2 == 0 %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}Flask
html檔案
HTTP Method

HTTP Method

假設今天我們要把一個東西從前端傳到後端
那我們要怎麼達到呢?
(沒是我只是想秀我setup和btr的東西)
資訊(如我空格輸入的東西)
他要這個資訊


HTTP Method
我們主要是用兩種方式
| 方法 | get | post |
|---|---|---|
| 如何 | 直接加在URL後 head |
傳送資料另外寫 body |
| 好處 | 便宜 | 安全 |
| 壞處 | 不安全 | 貴 |
| 屬性 | request.args | request.form |
沒有哪個一定比較好 要看情況
Get Method
我們該如何get到資訊呢?
直接加在URL後就好了!
http://127.0.0.1:5000/get_example?name=ming&email=ming@example.com
http://127.0.0.1:5000/get_example/ming/ming@example.com
都可以取得兩個資訊
那兩個有什麼區別呢?
Get Method
| Query String (?a=5&b=10) | Path Variables (/5/10) | |
|---|---|---|
| 傳遞方式 | request.args.get() | <int:a>、<int:b> |
| 適合場景 | 搜尋、可選參數 | 固定結構的 API |
| URL 範例 | /add?a=5&b=10 | /add/5/10 |
| 可選參數 | 可以省略(使用 default) | 不能省略,必須填值 |
| 可讀性 | 較靈活,適合不固定的查詢 | 較清楚,適合固定格式 |
假設我們要用get method拿到兩個數字
有兩種不同的方法
Form
通常來說都是用html中的<form>來傳送資訊
(你們昨天應該有學到table吧)
<h2>get method:</h2>
<form action="/get_example" method="get">
<table border="1">
<tr>
<th>欄位</th>
<th>輸入</th>
</tr>
<tr>
<td>姓名</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>電子郵件</td>
<td><input type="email" name="email"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="送出"></td>
</tr>
</table>
</form>
get:
後端找的內容
跟表單說這是get
Form
通常來說都是用html中的<form>來傳送資訊
(你們昨天應該有學到table吧)
<h2>post method</h2>
<form action="/post_example" method="post">
<table border="1">
<tr>
<th>欄位</th>
<th>輸入</th>
</tr>
<tr>
<td>使用者名稱</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密碼</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交"></td>
</tr>
</table>
</form>
post:
後端找的內容
跟表單說這是post
Form
get
from flask import Flask
from flask import redirect
from flask import request, render_template
app = Flask(__name__)
@app.route("/")
def main():
return render_template("index.html")
@app.route("/get_example", methods=['GET']) #form把資訊傳到 "/get_example", 我們和flask說這是get method
def get_example():
if request.args:
#取得我們form的資料(在URL中)
name = request.args.get('name')
email = request.args.get('email')
return f'<h2>{name} / {email} </h2>'
else:
return redirect("http://127.0.0.1:5000/") #如果沒收到,就回到主畫面
if __name__ == '__main__':
app.run(debug=True,port=5000)Form
get

可以看到 get的資訊都在URL裡面
Form
post
from flask import Flask
from flask import redirect
from flask import request, render_template
app = Flask(__name__)
@app.route("/")
def main():
return render_template("index.html")
@app.route("/post_example", methods=['GET', 'POST']) #form把資訊傳到 "/post_example", 我們和flask說這是post method
def post_example():
if request.method == 'POST':
#取得我們form的資料
name = request.form['username']
email = request.form['password']
return f'<h2>{name} / {email} </h2>'
else:
return redirect("http://127.0.0.1:5000/") #如果沒收到,就回到主畫面
if __name__ == '__main__':
app.run(debug=True,port=5000)Form
post
為什麼還要 'GET' 呢?
如果我們在沒有資料的情況下直接開啟網址
(例如直接打入網址,或把東西都寫在同個route底下)
那後端收到的第一次就會是 get method
而這樣才能正確的判斷 不出錯


HTTP Method
實作三
將剛剛講的表單輸入做出來
可以輸入你的名稱,email,密碼之類的東西
解答就是剛剛的扣(不要完全抄 自己寫寫看)
cookie/session
cookie?



cookie / session
http://127.0.0.1:5000/a
http://127.0.0.1:5000/b
架設我們今天要在route之間傳送資訊:
例如:登入狀態,偏好設定等
我們可以用cookie和session達到!
cookie

cookie是伺服器留給使用者的一些資訊
讓我們在載入其他route時,後端可以取來使用的資訊
cookie
怎麼知道這個cookie不是使用者自己簽的?
(不是伺服器給的)

加密 AES
數字簽名 RSA
session
在後端Flask存的Cookie
= session
經過簽名的cookie
session
| Cookie | Session | |
|---|---|---|
| 存在哪裡? | 使用者 (Client 端) |
伺服器 (Server 端) |
| 安全性 |
不安全 容易被竊取、修改 |
安全 因為存放在伺服器 |
| 適用情境 | 記住登入狀態、偏好設定 (較不重要的東西) |
會員登入、銀行交易 (需要更高安全性) |
session
from flask import Flask, session
app = Flask(__name__)
secret_key = 'c4dffa417abe4d31936cdf52d3a6d7ae'
app.config['SECRET_KEY'] = secret_key #用來加密的
#有一個 session 的 dict,可以儲存一個使用者的資訊,並且在route之間溝通
@app.route('/login')
def login():
session['login'] = True
return 'Login success'
@app.route('/logout')
def logout():
session['login'] = False
return 'Logout success'
@app.route('/main')
def profile():
if session.get('login'): #login
return 'Main page'
else: #not login
return 'User not logged in'
if __name__ == '__main__':
app.run(debug=True, port=5000)# 生成一個隨機的 secret key
import uuid
print(uuid.uuid4().hex)session
實作四
建立一個前端能輸入 使用者名稱跟密碼
他打什麼使用者名稱就把他存到 session 裡
並且做出一個admin介面
只有使用者名稱為admin和密碼符合才可查看
簡單來說就是建立一個簡單的登入頁面
session
解答
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>post method</h2>
<form action="/post_example" method="post">
<table border="1">
<tr>
<th>欄位</th>
<th>輸入</th>
</tr>
<tr>
<td>使用者名稱</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密碼</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>session
解答
from flask import Flask
from flask import redirect
from flask import request, render_template, url_for, session
app = Flask(__name__)
secret_key = 'c4dffa417abe4d31936cdf52d3a6d7ae'
app.config['SECRET_KEY'] = secret_key #用來加密的
@app.route("/")
def main():
return render_template("index.html")
@app.route("/post_example", methods=['GET', 'POST']) #form把資訊傳到 "/post_example", 我們和flask說這是post method
def post_example():
if request.method == 'POST':
#取得我們form的資料
#把我們input的資料存到session
session['name'] = request.form['username']
session['password'] = request.form['password']
#判斷我們的輸入是否正確
if session['name'] == 'admin' and session['password'] == 'admin':
return redirect(url_for('success'))
else:
return redirect(url_for('fail'))
else:
return redirect("http://127.0.0.1:5000/") #如果沒收到,就回到主畫面
@app.route("/success")
def success():
#這裡用session,表示可以在路由之間傳送資訊
return f"success! your name is {session['name']}, and your password is {session['password']}"
@app.route("/fail")
def fail():
return f"login failed, please try again"
if __name__ == '__main__':
app.run(debug=True,port=5000).env
.env
功能
讓重要或敏感資料(不能公開的)不顯示在程式碼上
如資料庫密碼、DcBot token
.env

2. 創建檔案:
一定要一模一樣
(對他是沒有名字的檔案 然後副檔名是 .env)
pip install python-dotenv1. 下載python-dotenv:
.env
格式:
變數 = 值
變數 = "字串"example:
secret_code = 67
password = "password".env

假設要丟到github上
那要怎麼隱藏env檔

1. 創建 .gitignore 檔 (對名字要一模一樣)

.env
如何使用
from flask import Flask
import os
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
@app.route("/")
def main():
pw = os.getenv("password")
return f'password is: {pw}'
if __name__ == '__main__':
app.run(debug=True,port=5000)password = "password".env
實作五
用 .env 檔存一個變數
在用flask把他顯示出來
(例如可以存存看剛剛的secret_key)
api


假設我們要從前端取後端的資訊,或是從其他伺服器拿資訊的化,我們要怎麼作呢?
(對我用了同張圖)
前端 (or 後端)
後端 (or 其他伺服器)


api
api

簡單來說,我們可以用api,從其他伺服器(或自己的)拿取資料
api
我們從自己的後端開一個api,然後再從前端的js拿取:
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route('/api_get', methods=['GET'])
def api_get():
name = request.args.get('name')
email = 'placeholder' #(可以從name拿一些資訊 啊我懶得做了 你們可以自己想想看)
return jsonify({'message': f'Hello, {name}! Your Email is {email}.'})
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True, port=5000)後端部分:
api
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Class</title>
</head>
<body>
<!--這裡可以有些輸入,像是要傳給後端的資料-->
<input id="name">
<button onclick="send_get_request()">click to get information!</button>
<p id="response"></p>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
</html>前端部分:
function send_get_request(){
//避免特殊字元(像是空格、&、=、/ 等)破壞網址格式。
let name = document.getElementById("name").value;
//encodeURIComponent() 是用來對使用者輸入的做 URL 編碼
fetch(`/api_get?name=${encodeURIComponent(name)}`)
.then(res => res.json()) //回應為 JSON
.then(data => document.getElementById("response").innerText = data.message) //把那個資訊(data) 變成 #response 的文字
.catch(err => console.error("錯誤:", err)); //如果錯誤的話
}api
實作六
建立一個和剛剛類似,可以從前端輸入名字,
然後取得一些使用者資料的網站
(答案在上面,我懶得在用了 :P)
api
補充
在python 取得其他api的資訊
舉例來說:discord的api
每個api 通常來說都有 docs,
他會跟你說要輸入什麼,然後他會回傳什麼
api
舉例來說:用discord bot取得使用者資訊
import requests
API_ENDPOINT = "https://discord.com/api/v10"
def exchange_code(code):
# gets the access_token
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redir
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(client_id, client_secret))
r.raise_for_status()
return get_user_data(r.json()['access_token'])
def get_user_data(accessToken):
# gets the user id from the access_token
headers = {
"Authorization": f"Bearer {accessToken}"
}
r = requests.get(url=api_url, headers=headers)
r.raise_for_status()
return r.json()
這兩個是dc bot的資訊
我們從這個api的網址request資訊
(開不懂沒關係 通常這種東西都用抄的)
課程結束
幾樣我沒講到
但其實也算重要的東西:
Data Base
websocket
blueprint
祝你們寫後端好運!
Flask
By MLGnotCOOL
Flask
- 89