# Lab13 網頁架構、Flask建立Web API ###### tags: `2022計概` `學生版` 助教:宏恩 [課程影片](https://youtu.be/_DocxNFNQtM) ## 網路的架構-PBL應用 - 路徑規劃和任務分發: 通過網路連接,您可以向無人載具發送路徑規劃和任務分發命令。這意味著您可以在遠程位置為載具指定特定的任務,並調整其運行路徑,以適應變化的工廠或倉庫布局。這提高了操作的靈活性和效率。 - 數據收集和分析: 通過網路連接,無人載具可以將感測數據和任務日誌傳輸到遠程伺服器或雲端平台。這樣的數據可以用於分析性能、追蹤歷史運行數據、進行預測性維護以及優化生產流程。這對於提高效率和生產力非常重要。 ## 網頁概論 * 從點下某個網頁到呈現,中間發生了什麼事? ![](https://i.imgur.com/h6MrLjo.png) * 前端「主要」的工作就是「呈現」 * 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架構 ![](https://i.imgur.com/H7EqZeq.png) * bootstrap <!-- https://startbootstrap.com/theme/sb-admin-2 --> ### request ![](https://i.imgur.com/99nVjhC.png) * Get 單純的跟 server 要一個連結或圖片,通常網頁都是 Get 的 request 比較多 最常使用的 Method 例如:要去某個網址、看某張圖片 傳送的資料會以 Query String 的方式加在 url 上 * Post 需要執行一些私人資料的傳輸時,會傳送 Post request * 例如:登入會員、送出表單 獲取「指定的」資訊,放在 request body(Form data)裡面 * 注重隱私的資料傳輸方法,因為他不會將資料顯示在url上 ### response * status code ![](https://i.imgur.com/HrYH0bw.png) 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 ``` ![1701016294515](https://hackmd.io/_uploads/HydFXlWBT.jpg) ### 如果沒有,才需執行執行已下命令去安裝 ``` python= pip install flask ``` ![](https://i.imgur.com/1ttqPQy.png) ### 路由(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 (進入首頁,輸入表單內容,按下送出) ![1701010083014](https://hackmd.io/_uploads/BkZX6ReBT.jpg) * basic_2 (terminal顯示輸入資料) ![1701010190120](https://hackmd.io/_uploads/rytPT0xBp.jpg) * basic_3 (後端回傳ok至網頁,會與basic_2幾乎同時顯示) ![1701010219363](https://hackmd.io/_uploads/BJAupCgBT.jpg) * basic_4 (在瀏覽器輸入 " http://127.0.0.1:3000/rsp?choice=r " 來發送要求,與後端玩剪刀石頭布,並顯示結果在網頁,結果為隨機的) ![1701010330965](https://hackmd.io/_uploads/SyhWCRgHT.jpg) * basic_5 (驗證 : 在terminal顯示雙方的選擇) ![1701010605059](https://hackmd.io/_uploads/SkVK0AeH6.jpg) * basic_6 (玩第二次) ![1701010655532](https://hackmd.io/_uploads/ryWlJy-ST.jpg) * basic_7 ![1701010673466](https://hackmd.io/_uploads/Bk-rykbBa.jpg) * basic_8 (輸入有誤的情況,請將回傳 " 輸入有誤,請重新出拳 " 的部分改成回傳 " Wrong input ! try again "給前端,terminal不用顯示東西) ![1701101318672](https://hackmd.io/_uploads/ry0ryrzB6.jpg) * 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會顯示後端存放之資料 ) ![1701011606274](https://hackmd.io/_uploads/HyjNMy-HT.jpg) * plus_2( 在termnal顯示使用者在前端輸入之資料 以及 確定資料有存進後端的字典裡 ) ![1701011663006](https://hackmd.io/_uploads/SkeOXybS6.jpg) * plus_3 (current data 顯示現在後端已存放之資料,會是以字典的模樣呈現) ![1701011683673](https://hackmd.io/_uploads/SJ4YQJWSp.jpg) * plus_4(以此類推) ![1701014515913](https://hackmd.io/_uploads/By_ShJWHT.jpg) * plus_5(以此類推) ![1701011840036](https://hackmd.io/_uploads/SJf54JZS6.jpg) * plus_6(在瀏覽器輸入 " http://192.168.137.227:3000/reset/y " 發送要求,讓後端清除已存放之資料,前端會顯示 Reset以及回首頁的超連結) ![1701011914239](https://hackmd.io/_uploads/ry5eBkZHT.jpg) * plus_7(驗證使否有清除資料) ![1701012971919](https://hackmd.io/_uploads/ByT-8kZHp.jpg) * 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