# 自主學習: Python Flask 與 Web ## 序和介紹 ### 哈囉各位(偷偷給自己打個廣告) * Insgram: @[chao28661](https://www.instagram.com/chao28661/) * 我ㄉ自介: https://ncves.com/ * contact me with email: peer@ncves.net #### 當前職況 * 中部高中電資聯合會議-資訊組 > !!插個小廣告!! > 目前正在招收第三屆成員,如果對電資有興趣歡迎在6/17前和我聯絡([ig](https://www.instagram.com/chao28661/)或[email](mailto:peer@ncves.net)都可以),我會幫你引薦~~ * 今年寒訓ㄉ教學組兼攝影 * 今年7/2幹訓ㄉ資訊安全講師兼攝影 * RIPE(歐洲網際網路資源協調中心)ASN [AS199331](https://irrexplorer.nlnog.net/asn/AS199331) * 某網路訓練中心創辦人 #### 擅長區塊 * 資訊安全 * Python腳本開發 * 網頁編寫、美化 * Linux作業系統操作 * 遊戲伺服器搭建(Minectaft、FiveM) * 網站、網域部屬 * 網路BGP ## 簡報前言 動機與目的: 1. 提升程式語言編寫能力,掌握前後端開發 內容摘要: 1. 前言 1. 環境架設 1. 功能測試 所遇挑戰與克服過程 1. 一開始在處理POST回傳值時一直跳==405 Method Not Allowd==,處理很久後才發現是html的action沒設定好,將它設定好後再運行一次就正常ㄌ 反思與新的學習及收穫 1. 這次最大的收穫就是掌握了後端開發的領域,未來想轉向用JavaScript開發後端 ### 為什麼要學習前後端資料互傳? > Q: 前後端資料互傳很重要? > > A: 是,而且是非常重要 > > 舉個例子: > 平時逛購物網站、IG都要登入自己的帳號,那後端系統是如何驗證你輸入的帳號密碼是否正確? 讓我們看下流程圖 ![](https://hackmd.io/_uploads/SyjJ_Dmvh.png) 在這張圖中,最左邊是我們一般使用者 假設他現在想登入某購物網站 他在登入頁面填上的帳密為admin, 1234 他輸入的資料將會以==POST==的==封包==傳遞方式傳給後端伺服器 此時後端伺服器會將他輸入的值拿去和資料庫比對 這時會有2種狀況: 1. 比對相符(成功登入) 1. 比對不符合(登入失敗) ## 環境搭建 必需安裝的東西有以下: 1. Python 1. Python Flask ### 安裝Python ![](https://hackmd.io/_uploads/SJIbcv7v2.png) 前往Python官網下載 https://www.python.org/downloads/ 其實這邊除了用瀏覽器到官網下載,還有另一種酷酷的安裝方法: > 這邊先介紹一個酷東東 > ![](https://hackmd.io/_uploads/S1Jh9P7v2.png) > 這是一個內建在Windows預設的工具,無須另外下載,直接搜尋即可 > 這是我們俗稱的PowerShell,他類似==Linux==的==Terminal==,他擁有豐富的==指令介面(CLI)== 打開後輸入: ``` winget install python ``` 就會開始自動下載ㄌ 以下這些是一些其他用法 ![](https://hackmd.io/_uploads/H1lEWCrPh.png) ### 安裝Flask 打開PowerShell後應該會看到以下介面 ![](https://hackmd.io/_uploads/B1StowQwh.png) 接著我們輸入 ``` pip install flask ``` 會跑出類似這樣的東東(我的已安裝過) ![](https://hackmd.io/_uploads/S1FajwXD2.png) ### 搭建資料庫 這次是使用一款輕量級的資料庫軟體叫做sqlite3 這款軟體也是內建ㄉ,不須而外下載 我們可以透過一個python檔案快速完成布署這個資料庫方便後續使用 ```python= import sqlite3 connect = sqlite3.connect("database.db") sql = connect.cursor() sql.execute('CREATE TABLE IF NOT EXISTS database (USERNAME TEXT, PASSWORD TEXT)') connect.commit() connect.close() ``` ### 其他好用ㄉ軟體 1. [Visual Studio Code](https://code.visualstudio.com/Download) - 方便程式碼撰寫 3. [SQLiteBrowser](https://sqlitebrowser.org/dl/) - 方便觀察資料庫 ## 開始編寫 ### 前言 前言: 對於了解這部分的人應該會有些疑問: > Q1: 比起使用Python撰寫後端邏輯,為何不使用JavaScript或php等等更適合的語言? > > A1: 問這個問題前,可以先往上看看我擅長的程式語言,在眾多的程式語言中,Python是我最拿手ㄉ。 > > Q2: Python Flask在寫後端邏輯上方便嗎? > > A2: 要回答這個問題前,我們先來了解甚麼是Jinja。 其實Jinja就是一套為了寫Python後端邏輯而開發的模板套件,他的出現解決了很多繁瑣的問題,也因為他的出現,現在很多人選擇使用Python Flask作為開發後端的語言。([有興趣點我查看更多](https://jinja.palletsprojects.com/en/3.1.x/)) 順帶一提Python Flask是一套輕框架,啟動他的環境僅僅需要以下7行程式碼 ```python= from flask import Flask app = Flask(__name__) @app.route("/index") def func(): return "Hello World" if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` ### 基礎網概 各位是否有個疑慮,在上面的最後一段程式碼中==host==、==port==代表什麼? ==host==: 這邊輸入的127.0.0.1代表什麼意思呢? > A: 在IPv4協定中,有一些被稱為保留段的IP,常聽到的有192.168.XXX.XXX或0.0.0.0等等,這邊輸入的127.0.0.1也是一段保留段,他代表的是當前本機 ==port==: 我們先看張url定位的規則 ![](https://hackmd.io/_uploads/SJMvX_NPn.png) 這邊的端口就是port的意思。如果要簡單比喻這個協定的話,我們可以把scaict.org這個網域想像成一間大飯店,這間"大"飯店非常大有1~65535間房間,每間都可以出租給客人(將每間想成會開服務) 回到port這個協定,我們只要在網域後面加上:XXXX就可以進入對應的服務 有一些為==TCP/UDP==系統預設占用端口,可以在這裡查看 https://zh.wikipedia.org/zh-tw/TCP/UDP%E7%AB%AF%E5%8F%A3%E5%88%97%E8%A1%A8 > 一些常見的有80(http)、443(https)、20/21(ftp)、22(ssh) ### 導入套件 開始寫任何程式碼的第一步想必都是導入相關套件 這次我們寫的是Python Flask,因此第一步就會是導入相關套件 ```python= from flask import Flask from flask import request from flask import render_template from flask import render_template_string import sqlite3 app = Flask(__name__) if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` > 如果覺得1~4行這樣寫太佔行數的話,可以改成以下: ```python= from flask import * ``` ### 網頁框架 套件導入完後讓我們接著繼續寫 接下來就是要寫網頁的框架,他看起來會像這樣 ```python= from flask import Flask from flask import request from flask import render_template from flask import render_template_string import sqlite3 app = Flask(__name__) #設定網頁路由 @app.route("/create") def create(): return "Hello World" if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` ### 連線資料庫 我們這次要寫的目標很明確,就是一套登入系統,說到登入系統,資料庫是必備的吧! ```python= from flask import Flask from flask import request from flask import render_template from flask import render_template_string import sqlite3 app = Flask(__name__) #設定網頁路由 @app.route("/create") def create(): #連線資料庫 connect = sqlite3.connect('database.db') sql = connect.cursor() if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` ### 前端門面 接下來我們要讓網頁能出現輸入的表單 ```python= from flask import Flask from flask import request from flask import render_template from flask import render_template_string import sqlite3 app = Flask(__name__) #設定網頁路由 @app.route("/create") def create(): #連線資料庫 connect = sqlite3.connect('database.db') sql = connect.cursor() #若傳輸協定為POST(網路概論內容),則觸發下面判斷式 if request.method == 'POST': return "Hello World" else: #將表單回傳到網頁上 return''' <form method="post" action="/create"> <p>創建帳號</p> <input type="text" name="username"> <p>創建密碼</p> <input type="text" name="password"> <input type="submit" name="submit" value="創建帳號"> </form> ''' if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` 寫完以上code後,我們連線到 127.0.0.1:5000/create 會看到以下東西 ![](https://hackmd.io/_uploads/Hkmgu1HD3.png) ### 後端邏輯 現在大致的門面(前端)都好了,讓我們來準備後端吧! 接下來的後端都要寫在判斷式為==POST==的裡面 根據邏輯,我們的目標就是要拿到使用者輸入的資料,但是不見得每個使用者都會按照我們開發時的原意去做,因此我們可以寫個python中的例外處理避免這類問題發生 ```python=20 #嘗試接收網頁回傳資料 try: username = request.values["username"] password = request.values["password"] #若沒成功接收回傳"請輸入帳密" except: return render_template_string("請輸入帳密") ``` 最後成功收到回傳值後,我們就可以將他寫入資料庫啦~~ ```python=30 #成功接收執行以下敘述 else: #判斷回傳資料是否為空字串 if len(username) == 0: return render_template_string("帳號請勿留白") if len(password) == 0: return render_template_string("密碼請勿留白") if len(username) and len(password) == 0: return render_template_string("帳號密碼請勿留白") #成功通過前面檢查則將回傳資料寫進資料庫 sql.execute(f"INSERT INTO database (USERNAME, PASSWORD) VALUES ('{username}', '{password}')") connect.commit() #回傳資串"帳號創建完畢" return render_template_string("帳號創建完畢!") ``` 這邊就展示創建帳號,登入帳號原理和創建帳號其實差不多XD 下面有源碼可以自行觀看~ ## 程式碼最終型態 ### main .py ```python= from flask import Flask from flask import request from flask import render_template from flask import render_template_string import sqlite3 app = Flask(__name__) #設定網頁路由 @app.route("/create", methods = ['GET', 'POST']) def create(): #連線資料庫 connect = sqlite3.connect('database.db') sql = connect.cursor() #若傳輸協定為POST(網路概論內容),則觸發下面判斷式 if request.method == 'POST': #嘗試接收網頁回傳資料 try: username = request.values["username"] password = request.values["password"] #若沒成功接收回傳"請輸入帳密" except: return render_template_string("請輸入帳密") #成功接收執行以下敘述 else: #判斷回傳資料是否為空字串 if len(username) == 0: return render_template_string("帳號請勿留白") if len(password) == 0: return render_template_string("密碼請勿留白") if len(username) and len(password) == 0: return render_template_string("帳號密碼請勿留白") #成功通過前面檢查則將回傳資料寫進資料庫 sql.execute(f"INSERT INTO database (USERNAME, PASSWORD) VALUES ('{username}', '{password}')") connect.commit() #回傳資串"帳號創建完畢" return render_template_string("帳號創建完畢!") else: #將表單回傳到網頁上 return''' <form method="post" action="/create"> <p>創建帳號</p> <input type="text" name="username"> <p>創建密碼</p> <input type="text" name="password"> <input type="submit" name="submit" value="創建帳號"> </form> ''' #設定網頁路由 @app.route("/login", methods = ['GET', 'POST']) def login(): #連線資料庫 connect = sqlite3.connect('database.db') sql = connect.cursor() #若傳輸協定為POST(網路概論內容),則觸發下面判斷式 if request.method == 'POST': #嘗試接收網頁回傳資料 try: username = request.values["username"] password = request.values["password"] #若沒成功接收回傳"請輸入帳密" except: return render_template_string("請輸入帳密") #成功接收執行以下敘述 else: #判斷回傳資料是否為空字串 if len(username) == 0: return render_template_string("帳號請勿留白") if len(password) == 0: return render_template_string("密碼請勿留白") if len(username) and len(password) == 0: return render_template_string("帳號密碼請勿留白") #驗證輸入是否等於資料庫存在的資料 user = sql.execute(f"SELECT USERNAME FROM database WHERE USERNAME = '{username}' AND PASSWORD = '{password}'") #驗證資料庫中是否存在該項 if user.fetchone() is None: return render_template_string("請輸入有效帳號密碼") #調出使用者輸入的帳號名稱 user_name = user.fetchone() #導向welcome.html且在welcome.html上的username變數顯示使用者登入時輸入的帳號 return render_template("welcome.html", username = user_name) return''' <form method="post" action="/login"> <p>登入帳號</p> <input type="text" name="username"> <p>登入密碼</p> <input type="text" name="password"> <input type="submit" name="submit" value="登入"> </form>''' if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True) ``` ### templates/welcome.html ```html= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登入</title> </head> <body> <p>Hello,</p> <p>你已成功以 {{ username }} 登入</p> </body> </html> ``` ## 功能測試 ### 開始運作 使用==Terminal==或其他東西讓他跑起來~~ (這邊使用Visual Studio Code內建的Terminal) ![](https://hackmd.io/_uploads/BkWONZ8w3.png) 啟動後我們可以連上 127.0.0.1:5000/ ### 創建帳號 連上 127.0.0.1:5000/create 進入頁面後,填入想創建的帳號及密碼 ![](https://hackmd.io/_uploads/SkBlHWUDh.png) 按下創建帳號後,我們前往資料庫查看會發現以下: ![](https://hackmd.io/_uploads/S1SfSZLD2.png) 看到這列代表程式已成功將資料寫進資料庫了 這樣就創建完ㄌㄡ~ ### 登入帳號 連上 127.0.0.1:5000/login 我們利用剛剛創建的那組帳密來嘗試登入看看! ![](https://hackmd.io/_uploads/HJW9rZUwn.png) 按下登入後我們可以發現頁面跳到成功登入了 ![](https://hackmd.io/_uploads/BJynHZIP3.png) 剛才是成功登入,我們接下來試試看將帳號密碼輸入錯誤會怎樣 我們故意輸入錯誤的密碼 ![](https://hackmd.io/_uploads/rkkkUbUD3.png) 按下登入後發現他回傳了登入失敗的訊息 ![](https://hackmd.io/_uploads/B1iWIZUD2.png) ## 結束~~ 感謝各位聆聽 (偷偷告訴你,這是github開源專案,快到這裡查看吧!!) [點我傳送](https://github.com/Charlie28661/Password-validation) ![](https://hackmd.io/_uploads/SkPTluXvh.png)