http://www.javashuo.com/article/p-vzmjtyxq-ke.htmlhtml
昨天的做業就是,有3個視圖函數,分別是/login,/student_list,/student_detail。寫一個裝飾器,除了/login之外,其餘視圖函數都要登陸才行!前端
使用session判斷!django
原始代碼json
from flask import Flask,render_template,sessions,request,redirect app = Flask(__name__) USER = {'username': 'xiao', 'password': "123"} @app.route("/login",methods=["POST","GET"]) def login(): if request.method == "GET": # 前端模板中使用了msg,這裏就算是傳遞空,也要出現msg return render_template("login.html", msg="") if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: return redirect("/student_list") return render_template("login.html", msg="用戶名密碼錯誤") @app.route("/student_list") def student_list(): return "學生列表" @app.route("/student_detail") def student_detail(): return "學生詳情" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
使用裝飾器flask
from flask import Flask,render_template,session,request,redirect app = Flask(__name__) # 使用session,必須設置secret_key app.secret_key = "123asdzxc" USER = {'username': 'xiao', 'password': "123"} def auth(func): def inner(*args,**kwargs): if session.get("user"): return func(*args,**kwargs) else: return redirect("/login") return inner @app.route("/login",methods=["POST","GET"]) def login(): if request.method == "GET": # 前端模板中使用了msg,這裏就算是傳遞空,也要出現msg return render_template("login.html", msg="") username = request.form["username"] # 獲取POST請求時,FormData中的參數 # password = request.form.get("password") if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: # 設置session session["user"] = username return redirect("/student_list") return render_template("login.html", msg="用戶名密碼錯誤") @app.route("/student_list",endpoint="student_list") @auth def student_list(): return "學生列表" @app.route("/student_detail",endpoint="student_detail") @auth def student_detail(): return "學生詳情" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
重啓flask,直接訪問student_list瀏覽器
http://127.0.0.1:5000/student_list
打開瀏覽器工具,查看網絡。它會跳轉至登陸頁面!緩存
輸入正確的用戶名和密碼安全
提交以後,跳轉頁面服務器
注意:使用裝飾器的視圖函數,必需要定義endpoint參數。cookie
由於使用裝飾器以後,視圖函數都是inner,因此flask沒法區分,路由到底指向哪個視圖函數。
啓動flask以後,會直接報錯。endpoint參數,是給url起別名,惟一標識。能夠作url反向解析!
還有一種方法,使用@functools.wraps裝飾器,保留原函數信息,好比函數名。
可是不推薦使用。由於定義視圖函數,自己就應該定義endpoint參數
一、使用裝飾器裝飾兩個視圖函數,代碼以下
from flask import Flask, redirect, render_template, request, session app = Flask(__name__) app.secret_key = "wanglili" # 裝飾器函數 def outer(func): def inner(*args, **kwargs): if session.get("user"): # 驗證session ret = func(*args, **kwargs) return ret else: return redirect("/login") return inner @app.route("/") @outer # inner=outer(index) def index(): return render_template("index.html") @app.route("/course") @outer def course(): return render_template("course.html") @app.route("/login", methods=["POST", "GET"]) def login(): if request.method == "GET": return render_template("login.html") if request.form.get("username") == "wll"and request.form.get("password") == "123": session["user"] = request.form.get("username") # 寫入session return redirect("/") return render_template("login.html") app.run(debug=True)
啓動程序有以下錯誤:
咱們還發現當裝飾一個視圖函數時能夠正常運行,而裝飾兩個或兩個以上視圖函數則會報以上錯誤。
二、解決方式
1)方式一:使用functools模塊
import functools def outer(func): @functools.wraps(func) # 保留原函數func的信息 def inner(*args, **kwargs): if session.get("user"): ret = func(*args, **kwargs) return ret else: return redirect("/login") return inner
2)方式二:使用flask提供的endpoint參數
@app.route("/", endpoint='index') @outer def index(): return render_template("index.html") @app.route("/course", endpoint='course') @outer def course(): return render_template("course.html")
Flask中的路由系統其實咱們並不陌生了,從一開始到如今都一直在應用
@app.route("/",methods = ["POST","GET"])
爲何要這麼用?其中的工做原理咱們知道多少嗎?
一、@app.route()裝飾器中的參數
若是不明白裝飾器 點擊這裏
methods : 當前 url 地址,容許訪問的請求方式
from flask import Flask,request app = Flask(__name__) @app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
訪問url,注意:要帶上參數id,不然報錯
http://127.0.0.1:5000/info?id=1
效果以下:
endpoint:反向url地址,默認爲視圖函數名 (url_for)
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新頁面,效果同上!查看Pycharm控制檯輸出:
/info
注意:這並非完整的url。若是要給前端妹子,返回一個完整的URL怎麼辦呢?
url_for:用於反向生成url,也能夠附帶一些參數,好比想要完整的URL,能夠設置_external爲Ture:
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): stu_id = int(request.args["id"]) print(url_for("r_info", _external=True)) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新頁面,效果同上!查看Pycharm控制檯輸出:
http://127.0.0.1:5000/info
可是還不夠,參數沒有啊?怎麼辦?再加上url參數。
注意:因爲id是動態參數,因此後臺獲取時,要和實際的參數匹配。這裏是id
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): stu_id = int(request.args["id"]) print(url_for("r_info", _external=True,id=stu_id)) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新頁面,效果同上!查看Pycharm控制檯輸出:
http://127.0.0.1:5000/info?id=1
這下,就很是完美了!
defaults : 視圖函數的參數默認值{"nid":100}
注意:視圖函數必需要設置形參nid,不然報錯!
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info",defaults={"nid": 100}) def student_info(nid): # stu_id = int(request.args["nid"]) print(url_for("r_info", _external=True)) print(nid) return f"hello kitty {nid}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
訪問url: http://127.0.0.1:5000/info
效果以下:
strict_slashes : url地址結尾符"/"的控制 False : 不管結尾 "/" 是否存在都可以訪問 , True : 結尾必須不能是 "/"
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", strict_slashes=True) def student_info(): return "hello kitty info" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
末尾不帶 "/"
末尾帶 "/"
也就是說:strict_slashes = True ,表示開啓路由嚴格匹配模式!即便末尾多了一個"/",也不容許訪問!
將參數改成False
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", strict_slashes=False) def student_info(): return "hello kitty info" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新頁面,又能夠訪問了
若是多加一個s,會報404
總結:strict_slashes 用來控制末尾是否有"/",爲Ture,帶 "/"不容許!
爲False,無論你帶不帶"/",均可以訪問!
redirect_to : url地址重定向
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", redirect_to="/infos") def student_info(): return "hello kitty info" @app.route("/infos") def student_infos(): return "Hello Tom infos" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
訪問url: http://127.0.0.1:5000/info,會發生重定向
查看瀏覽器工具,查看網絡。它經歷了2次請求!
它有什麼應用場景呢?
好比你的網站域名是xx.com,後來你的網頁改版了,換了新的域名qqxx.com。可是老客戶還不知道你的新域名啊!怎麼辦呢?用重定向就能夠了!
subdomain : 子域名前綴 subdomian="qq" 這樣寫能夠獲得 qq.xx.com 前提是app.config["SERVER_NAME"] = "xx.com:5000"
from flask import Flask,request,url_for app = Flask(__name__) # 必定必定必定要寫端口!!!!!! app.config["SERVER_NAME"] = "xx.com:5000" @app.route("/",endpoint="r_info",subdomain="qq") def student_info(): print(url_for("r_info", _external=True,)) return "hello kitty info" if __name__ == '__main__': # 監聽端口爲5000,注意:要和SERVER_NAME匹配! app.run("0.0.0.0", 5000, debug=True)
注意:app.config["SERVER_NAME"] = "xx.com:5000"。
必定要加端口!必定要加端口!必定要加端口!重要的事情說三遍!!!
就是由於沒有加端口,快要放棄了!google了一把,終於找到緣由了!
這裏是window 10訪問。必需要修改本機的Hosts文件,至於怎麼修改,爲啥沒有權限,請自行百度!
修改Hosts文件,添加一條記錄
127.0.0.1 qq.xx.com
打開cmd窗口,ping qq.xx.com,請確保返回地址是127.0.0.1
打開瀏覽器訪問url,注意:只能是qq.xx.com訪問。不能是xx.com!訪問時,必定要帶上端口!
http://qq.xx.com:5000/
效果以下:
關於路由目前就說這麼多,以後的課程中會有關於Flask路由系統的源碼剖析,再詳細說明Flask路由系統的工做原理
<int:nid> 就是在url後定義一個參數接收
可是這種動態參數路由,在url_for的時候,必定要將動態參數名+參數值添加進去,不然會拋出參數錯誤的異常
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info/<int:nid>", endpoint="r_info") def student_info(nid): print(url_for("r_info", _external=True,nid=nid)) return f"hello kitty {nid}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': # 監聽端口爲5000 app.run("0.0.0.0", 5000, debug=True)
訪問url,必定要帶上參數,並且參數必須是數字!
查看Pycharm控制檯輸出:
12 <class 'int'> http://127.0.0.1:5000/info/12
若是是字符串,會報錯
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info/<string:nid>", endpoint="r_info") def student_info(nid): print(nid,type(nid)) print(url_for("r_info", _external=True,nid=nid)) return f"hello kitty {nid}" # Python3.6的新特性 f"{變量名}" if __name__ == '__main__': # 監聽端口爲5000 app.run("0.0.0.0", 5000, debug=True)
刷新頁面,就能夠訪問了
查看Pycharm控制檯輸出:
ask <class 'str'> http://127.0.0.1:5000/info/ask
參數是數字也是正常的
app = Flask(__name__) # Flask的實例化對象app
實例化對象時的參數以下:
template_folder="temp" 默認模板存放目錄 templates static_folder="static" 默認靜態文件存放目錄 static static_url_path="/static" 訪問靜態文件路由地址,默認是"/"+static_folder static_host=None 指定靜態文件服務器地址 host_matching = False 若不是特別須要時,慎用,不然全部的route 都須要host=""的參數 subdomain_matching = False 理論上來講是用來限制SERVER_NAME子域名的,可是目前尚未感受出來區別在哪裏 instance_path = None 指向另外一個Flask實例的路徑 instance_relative_config = False 是否加載另外一個實例的配置 root_path = None 主模塊所在的目錄的絕對路徑,默認項目目錄
Flask對象即Flask的實例化對象app,Flask的對象配置就是在 app.config 中添加鍵值對,例如,app.config["SECRET_KEY"]="/wrwfgs",可是你存進去的鍵必須是config中應該存在的,若是不存在的話,它會默認無用,因此咱們來看一下config中有哪些key以及對應的做用?
'DEBUG': False, # 是否開啓Debug模式 'TESTING': False, # 是否開啓測試模式 'SECRET_KEY': None # 在啓用Flask內置Session的時候/開啓flash,必定要有它 'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命週期(天)默認31天 'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字 'PROPAGATE_EXCEPTIONS': None, # 異常傳播(是否在控制檯打印LOG) 當Debug或者testing開啓後,自動爲True 'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一兩句話說不清楚,通常不用它 'SECRET_KEY': None, # 以前遇到過,在啓用Session的時候,必定要有它 'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命週期(天)默認31天 'USE_X_SENDFILE': False, # 是否棄用 x_sendfile 'LOGGER_NAME': None, # 日誌記錄器的名稱 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, # 服務訪問域名 'APPLICATION_ROOT': None, # 項目的完整路徑 'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字 'SESSION_COOKIE_DOMAIN': None, # 在哪一個域名下會產生session記錄在cookies中 'SESSION_COOKIE_PATH': None, # cookies的路徑 'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否應被設置 httponly 的標誌, 'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否應被設置安全標誌 'SESSION_REFRESH_EACH_REQUEST': True, # 這個標誌控制永久會話如何刷新 'MAX_CONTENT_LENGTH': None, # 若是設置爲字節數, Flask 會拒絕內容長度大於此值的請求進入,並返回一個 413 狀態碼 'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默認緩存控制的最大期限 'TRAP_BAD_REQUEST_ERRORS': False, # 若是這個值被設置爲 True ,Flask不會執行 HTTP 異常的錯誤處理,而是像對待其它異常同樣, # 經過異常棧讓它冒泡地拋出。這對於須要找出 HTTP 異常源頭的可怕調試情形是有用的。 'TRAP_HTTP_EXCEPTIONS': False, # Werkzeug 處理請求中的特定數據的內部數據結構會拋出一樣也是「錯誤的請求」異常的特殊的 key errors 。 # 一樣地,爲了保持一致,許多操做能夠顯式地拋出 BadRequest 異常。 # 由於在調試中,你但願準確地找出異常的緣由,這個設置用於在這些情形下調試。 'EXPLAIN_TEMPLATE_LOADING': False, # 若是這個值被設置爲True,你只會獲得常規的回溯。 'PREFERRED_URL_SCHEME': 'http', # 生成URL的時候若是沒有可用的 URL 模式話將使用這個值 'JSON_AS_ASCII': True, # 默認狀況下 Flask 使用 ascii 編碼來序列化對象。若是這個值被設置爲 False , # Flask不會將其編碼爲 ASCII,而且按原樣輸出,返回它的 unicode 字符串。 # 好比 jsonfiy 會自動地採用 utf-8 來編碼它而後才進行傳輸。 'JSON_SORT_KEYS': True, # 默認狀況下 Flask 按照 JSON 對象的鍵的順序來序來序列化它。 # 這樣作是爲了確保鍵的順序不會受到字典的哈希種子的影響,從而返回的值每次都是一致的,不會形成無用的額外 HTTP 緩存。 # 你能夠經過修改這個配置的值來覆蓋默認的操做。但這是不被推薦的作法由於這個默認的行爲可能會給你在性能的代價上帶來改善。 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None,
以上這些key均可以被改寫,固然它們也有默認值,若是沒有特殊狀況,不要改寫它的默認值。
除了單獨修改某一項的配置外,還能夠經過類的方式導入配置:
首先定義一個flask_settings.py,內容以下:
class FlaskDebug(object): # debug模式下的類 DEBUG=True SECRET_KEY="DEBUGmoshidesecret_key" PERMANENT_SESSION_LIFETIME = 7 SESSION_COOKIE_NAME = "debug_session"
而後再flask的啓動文件中使用,以下:
from flask import Flask app = Flask(__name__) app.config.from_object(flask_settings.FlaskDebug) # 配置debug模式
做爲初學flask的咱們,能夠將藍圖理解爲沒有run方法的Flask對象,接下來學習如何建立以及配置藍圖。
首先建立一個目錄flask_project/app01,在app01目錄中建立一個views.py文件,內容以下:
from flask import Blueprint # 從flask中導入藍圖 bp1 = Blueprint("bp1", __name__) # 實例化一個藍圖對象 @bp1.route('/stuList') # 添加路由並定義視圖函數 def stulist(): return "student list"
在flask_project目錄中建立manage.py文件,內容以下:
from flask import Flask from app01 import views # 導入剛寫好的藍圖所在模塊 app = Flask(__name__) app.register_blueprint(views.bp1) # 在Flask對象中註冊藍圖對象,重要 if __name__ == '__main__': app.run(debug=True)
此時,運行manage.py文件啓動服務,而後訪問http://127.0.0.1:5000/stuList,便可看到返回結果。
上述藍圖的視圖函數返回了一個HttpResponse對象,那麼是否能夠返回模板頁面呢?固然能夠,實例化藍圖(Blueprint)對象時,也能夠配置藍圖的模板目錄(template_folder參數)和靜態文件目錄(static_folder參數),也就說藍圖的對象配置與Flask的對象配置方法同樣,在此不過多介紹。
須要特別說明的是藍圖能夠指定url前綴(url_prefix參數),方法以下:
方法一:實例化藍圖對象時指定url前綴
bp1 = Blueprint("bp1", __name__, url_prefix='/app01')
方法二:在註冊藍圖時指定url前綴
app.register_blueprint(views.bp1, url_prefix='/app01')
此時,瀏覽器要訪問地址http://127.0.0.1:5000/app01/stuList
一、flask的中間件
1)@app.before_request # 請求進入視圖函數以前,相似於django中間件的request_process
2)@app.after_request # 響應返回客戶端以前,相似於django中間件的response_process
manage.py代碼以下:
from flask import Flask app = Flask(__name__) @app.before_request def be1(): print('我是be1') @app.before_request def be2(): print('我是be2') @app.before_request def be3(): print('我是be3') @app.after_request def af1(response): print('我是af1') return response @app.after_request def af2(response): print('我是af2') return response @app.after_request def af3(response): print('我是af3') return response @app.route('/index') def index(): print('我是視圖函數') return 'ok' if __name__ == '__main__': app.run(debug=True)
啓動項目,訪問http://127.0.0.1:5000/index,打印結果以下:
我是be1
我是be2
我是be3
我是視圖函數
我是af3
我是af2
我是af1
分析:@app.after_request自上而下依次執行,@app.after_request自下而上依次執行。與django的中間件相似,@app.after_request裝飾的函數不寫return或者returnNone表示放行,順利經過,不然攔截請求,返回響應。@app.after_request裝飾的函數必定要有返回值,且必須有參數接收response對象。
修改be1函數爲以下代碼:
@app.before_request def be2(): print('我是be2') return 'error'
執行結果以下:
我是be1 我是af3 我是af2 我是af1
總結:
正常狀況下流程:be1 - be2 - be3 - af3 - af2 - af1
異常(be1函數發生異常)狀況下流程:be1 - af3 - af2 - af1
二、重定義錯誤頁面返回信息
@app.errorhandler(404)# 參數是錯誤代碼 def error404(error_info):# 注意,必定要加參數接收錯誤信息 print(error_info) # 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. return '頁面不存在'# 能夠返回 三劍客 + 小兒子
當訪問時發生404錯誤時,會看到該函數返回的內容。