# Lab13 網頁架構、Flask建立Web API
###### tags: `2022計概` `學生版`
助教:宏恩
[課程影片](https://youtu.be/_DocxNFNQtM)
## 網路的架構-PBL應用
- 路徑規劃和任務分發: 通過網路連接,您可以向無人載具發送路徑規劃和任務分發命令。這意味著您可以在遠程位置為載具指定特定的任務,並調整其運行路徑,以適應變化的工廠或倉庫布局。這提高了操作的靈活性和效率。
- 數據收集和分析: 通過網路連接,無人載具可以將感測數據和任務日誌傳輸到遠程伺服器或雲端平台。這樣的數據可以用於分析性能、追蹤歷史運行數據、進行預測性維護以及優化生產流程。這對於提高效率和生產力非常重要。
## 網頁概論
* 從點下某個網頁到呈現,中間發生了什麼事?

* 前端「主要」的工作就是「呈現」
* HTML: 網頁的「骨架」。
* CSS: 描述如何「裝飾」網頁。
* JS: 讓你的菜單可以「互動」。
* 後端的工作就是「回應」前端需要的「資料」
* 處理各個需求(Request)並做出回應(Response)。
* 保存不同使用者的資料。
* 在網頁之中,前端與後端會是以 HTTP通訊協定 來傳輸資料
## 網址(Web address 或 URL)
### 用途
* 在瀏覽器上,用來連線到特定網路服務的地址
* 一個功能豐富的網站會是由各種網址組成
### 網址的組成
" 通訊協定://主機名稱:埠號/路徑?要求字串 "
* 網址範例( google 搜尋結果 )
* https://www.google.com/search?q=test
* 通訊協定 : https
* 主機名稱 : www.google.com
* 埠號 : 可以不顯示(https預設443)
* 路徑 : /search
* 要求字串 : q=test
* 通訊協定、主機名稱以埠號能夠讓瀏覽器(前端)尋找到指定之伺服器(後端),並連線
* 後端成功收到連線要求後,就會再依據路徑與要求字串來決定如何回應資料給前端
## MVC架構

* bootstrap
<!-- https://startbootstrap.com/theme/sb-admin-2 -->
### request

* Get
單純的跟 server 要一個連結或圖片,通常網頁都是 Get 的 request 比較多
最常使用的 Method
例如:要去某個網址、看某張圖片
傳送的資料會以 Query String 的方式加在 url 上
* Post
需要執行一些私人資料的傳輸時,會傳送 Post request
* 例如:登入會員、送出表單
獲取「指定的」資訊,放在 request body(Form data)裡面
* 注重隱私的資料傳輸方法,因為他不會將資料顯示在url上
### response
* status code

100 Continue:Server 成功接收、但 Client 還要進行一些處理
200 OK:成功
204 No Content:成功,但沒有回傳的內容( 例如當你發出 Delete 的 request )
301 Moved Permanently:資源「 永久 」移到其他位置,再下一次發出 request 時,瀏覽器會直接到新位置
302 Found(Moved Temporarily):資源「 暫時 」移到其他位置
304 Not Modified:東西跟之前長一樣,可以從快取拿就好
400 Bad Request:請求語法錯誤、或資源太大…等等
401 Unauthorized:未認證,可能需要登入或 Token
403 Forbidden:沒有權限
404 Not Found:找不到資源
500 Internal Server Error:伺服器出錯,搶票時很可能發生
501 Not Implemented
502 Bad Gateway:通常是伺服器的某個服務沒有正確執行
https://noob.tw/http-status-code/
## api
API 是什麼?
API 應用程式介面,是 Application Programming Interface 的縮寫。簡單來說,API 就如接收要求的信差、技術與數據的接口,是串接兩端裝置或應用程式的橋樑,創造連結,實現資訊或技術的交換、溝通。使用 API 能大幅減少開發成本,只需傳送需求,就能回傳資料,讓程式設計更便利。
Web API 是指藉由 http 通訊協定,進行請求、運算、回應的 API,提供 JSON、XML 兩種傳輸格式,基本上透過「網路」交換資訊,皆為 Web API 的範疇。
# flask
### 套件確認
```python=
flask --version
```

### 如果沒有,才需執行執行已下命令去安裝
``` python=
pip install flask
```

### 路由(Route)
* 決定網址中的路徑與處理函式的對應關係
* 以下為兩個網址:
* http://127.0.0.1/
* http://127.0.0.1/get_name
* 上面兩種網址所連接的後端為相同的,但是做不同的回應給前端呈現
* 會使用 **路由** 來決定不同路徑所對應之處理函式
## Get Request
### 基礎教學
```python=
#coding=utf-8
# 不加上就無法使用中文註解
from flask import Flask
app = Flask(__name__) #建立 flask 物件
# 路由設定
# 透過函式的裝飾器( @ )來設定路由
# 若有get request傳送到後端,就會執行他下面的function,function名稱隨意,但不可重複
# 設定根目錄(通常代表首頁)之路由
@app.route('/',methods=['GET'])
def index():
return "Hello World !"
# 設定路徑為 '/get_data'之路由
@app.route('/get_data',methods=['GET'])
def get_data():
return "Student_ID : N96124616"
# 將webserver執行,監聽任意來源ip,port開在3000,開啟debug模式
# debug模式代表,每次檔案更新後,webserver會自動重啟,不需要手動重啟
app.run(host="0.0.0.0", port=3000, debug=True)
```
### 動態路由
```python=
#coding=utf-8
# 不加上就無法使用中文註解
from flask import Flask
app = Flask(__name__)
# 設定根目錄(通常代表首頁)之路由
@app.route('/',methods=['GET'])
def index():
return "Hello World"
# 同樣設定路徑為 '/get_data'之路由,但使用動態路由設定
# 動態路由設定 : 使用<>來表示路由為需要被接收的變數,並且把該變數放到function的parameter中,就可以在function中使用這個變數
@app.route('/get_data/<student_id>',methods=['GET'])
def get_data(student_id):
return f'Student_ID : {student_id}'
# 將webserver執行,監聽任意來源ip,port開在3000,開啟debug模式
# debug模式代表,每次檔案更新後,webserver會自動重啟,不需要手動重啟
app.run(host="0.0.0.0", port=3000, debug=True)
```
### 要求字串( Query string )
```python=
#coding=utf-8
# 不加上就無法使用中文註解
from flask import request,Flask # 也可以直接打一個 * 就好,這樣代表import函式庫所有的函式 (from flask import *)
app = Flask(__name__)
# 設定根目錄(通常代表首頁)之路由
@app.route('/',methods=['GET'])
def index():
return "Hello World"
# 在網址中的路徑的後面加上?參數1=值1&參數2=值2......
# 範例 : http://127.0.0.1:3000/string_test?name=Tony
@app.route('/string_test',methods = ['GET'])
def string_test():
# 使用 request.args.get('參數明稱',預設值)
string1 = request.args.get('name',default= "Xiang")
return 'Hi ! '+string1
# 將webserver執行,監聽任意來源ip,port開在3000,開啟debug模式
# debug模式代表,每次檔案更新後,webserver會自動重啟,不需要手動重啟
app.run(host="0.0.0.0", port=3000, debug=True)
```
## Post Request
與前面使用的 Get Request不同,為了避免隱私性資料外洩,不會直接在url上做資料的輸入或顯示,通常會在前端設立表單來作為使用者輸入資料的窗口,表單需要使用html語言呈現
* html(前端)
``` html=
<!DOCTYPE html>
<html lang="en">
<!-- 注意 : 樹梅派上的html檔 ,請使用英文註解 ,否則會有解碼問題 -->
<!-- 基本的html架構,可以在vs code 裡 新增空的html檔,然後在第一行打 ! 再按 Tab鍵 即會幫你生成 -->
<!-- head 裡主要存放css code 去對網頁做排版以及美觀,但這次課程不會教,作業也不需要寫到,頂多需要修改title文字 -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST Example</title>
</head>
<!-- body區域,主要寫html的架構以及javascript(這次也不會用到), 這次作業會需要做修改的地方,主要是body裡的html code,以及可能會需要上網查一些相關用法 -->
<body>
<!-- 標題 -->
<h3>Simple Form</h3>
<!-- 水平分割線 -->
<hr>
<!-- 定義form表單 -->
<!-- action = 表單輸入並送出之後,要切換過去之路由,而表單內容基本上屬於比較私密之資料,因此會使用POST request -->
<form action='/set' method = 'POST'>
<!-- 輸入欄定義,懂都懂 -->
Text <input type="text" name="string1"/>
<!-- 定義一個按鈕,按下會送出表單內容,懂都懂 -->
<button>submit</button>
</form>
</body>
</html>
```
* python flask (後端)
注意 : 請在你執行python檔案的目錄底下新增一個名為 "templates" 的資料夾
``` python=
#coding=utf-8
# 不加上就無法使用中文註解
from flask import *
app = Flask(__name__)
# 設定根目錄(通常代表首頁)之路由
# 使用render_template函式讓首頁的路由能夠回傳html檔(簡單架構)給前端,來顯示首頁畫面,而不是僅用return文字的方式
@app.route("/")
def index():
# 回傳首頁畫面 ( 請確保你的html檔是放在名為 templates的資料夾)
return render_template('post_example.html')
# 設定路由為/set
# 使用 request.form['變數名稱'] 來直接取用表單的輸入資料
#
# 使用 request.form 來接收前端輸入之資料的資料,接著用to_dict()這個function來轉成python的dict格式,可以做資料的儲存
@app.route('/set',methods = ['POST'])
def root():
string1 = request.form['string1']
data = request.form.to_dict()
print(data)
return 'ok ! your text is : '+ string1 # 發送response為 ok + 傳送的文字內容
app.run(host="0.0.0.0", port=3000, debug=True)
```
## lab13
**作業繳交注意事項:**
1. 基礎題與加分題直接用一個資料夾上傳,資料夾名: 「lab10」。
4. 請注意指定的回傳內容。
5. 資料夾內需包含:程式碼。
6. 需要截圖,圖片中需要出現網頁畫面以及網址
7. 這次截圖比較多,請將圖片統一放在pdf檔裡,並分稱基礎題與加分題之pdf檔(晚點會上傳範例到discord)
8. 本次lab的 python檔 請加上備註(解釋你的程式碼在做啥)。
* 資料夾名稱:「lab10」
* 程式碼以及pdf檔名稱:「<a>lab10.py</a>」、 「lab10_plus.py」 、「lab10.html」、「lab10_plus.html」、「reset.html」、「lab10.pdf」、「lab10_plus.pdf」
* 作業繳交時間 : 12/05 23:59
## lab13 -基礎題
* 請依照前面的html範例,完成一個網頁,包含前端與後端,並具有以下API :
| api名稱 |method|傳入資料|功能 |
| -------- | -----|-------- | -----|
| / | GET|無|接收get request後,回傳 lab10.html至前端顯示首頁畫面,lab10.html會包含文字與表單,title為 " Basic Homework ",表單名稱為 " Student Data " |
|/student_data|POST|姓名(name)、學號(student_id)|依照上lab10.html,與表單互動,表單輸入姓名與學號,按下送出按鈕後,切換到/student_data路由,在server端的terminal顯示資料,並回傳 ' ok ' 給前端|
|/rsp|GET|choice|依照query string的方式,與後端玩剪刀石頭布,在url上輸入參數給後端做回應,範例 : " :http://127.0.0.1:5000/rsp?choice=r ",這樣代表你出石頭(rock),以此類推,如果輸入r、s、p以外的內容,則會顯示 " 輸入有誤,請重新出拳 "|
* 作業截圖呈現
* basic_1 (進入首頁,輸入表單內容,按下送出)

* basic_2 (terminal顯示輸入資料)

* basic_3 (後端回傳ok至網頁,會與basic_2幾乎同時顯示)

* basic_4 (在瀏覽器輸入 " http://127.0.0.1:3000/rsp?choice=r " 來發送要求,與後端玩剪刀石頭布,並顯示結果在網頁,結果為隨機的)

* basic_5 (驗證 : 在terminal顯示雙方的選擇)

* basic_6 (玩第二次)

* basic_7

* basic_8 (輸入有誤的情況,請將回傳 " 輸入有誤,請重新出拳 " 的部分改成回傳 " Wrong input ! try again "給前端,terminal不用顯示東西)

* Hint
* html如何換行 : 使用 " <br/> "
* 使用html的空白鍵作文字排版 : 語法請上網搜尋
## lab13 - 加分題
請完成一個網頁,包含前端與後端,並具有以下API :
| api名稱 |method|傳入資料|功能 |
| -------- | -----|-------- | -----|
|/|GET|無|接收到request之後,回傳 lab10_plus.html 至前端呈現首頁畫面, lab10_plus.html 同樣會包含文字與表單,title為 "Food Judge",表單的名稱為 "Score Chart"|
|/set|POST|店名(store_name)、分數(score)|在表單輸入店名與評分,一次只能輸入一組資料,但輸入完並送出之後,可以再繼續做輸入,輸入過的資料都會存入後端,請在後端設置一個字典來儲存輸入的資料,並在terminal顯示使用者輸入之資料以及字典當前之資料,同時也將字典資料回傳給前端呈現,呈現在current data那邊|
|/reset|GET|是或否(y or n)|使用動態路由的方式來要求清空後端的字典,輸入'y'時,後端會執行清空字典的動作,並顯示在terminal,並回傳reset.html至前端呈現畫面,畫面內需包含文字以及回首頁的超連結,其中文字為 " Reset ! " ,若輸入'n'或其他則會依然顯示首頁畫面,且current data應該會保持不變|
* 作業截圖呈現
* plus_1( 進入首頁,輸入表單內容,並按下送出,其中Current Data會顯示後端存放之資料 )

* plus_2( 在termnal顯示使用者在前端輸入之資料 以及 確定資料有存進後端的字典裡 )

* plus_3 (current data 顯示現在後端已存放之資料,會是以字典的模樣呈現)

* plus_4(以此類推)

* plus_5(以此類推)

* plus_6(在瀏覽器輸入 " http://192.168.137.227:3000/reset/y " 發送要求,讓後端清除已存放之資料,前端會顯示 Reset以及回首頁的超連結)

* plus_7(驗證使否有清除資料)

* Hint
* 如何回傳後端字典資料,請參考以下資料 :
* data_dict_string = json.dumps(data_dict,ensure_ascii = False) #將字典資料轉換成 json 格式的字串
* POST用法 : https://medium.com/seaniap/python-web-flask-get-post%E5%82%B3%E9%80%81%E8%B3%87%E6%96%99-2826aeeb0e28