Flask第十一篇裝飾器的坑及解決辦法、flask中的路由/實例化配置/對象配置/藍圖/特殊裝飾器(中間件、重定義錯誤頁面)

1、裝飾器的坑以及解決方法

一、使用裝飾器裝飾兩個視圖函數,代碼以下django

複製代碼
  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)
複製代碼

  啓動程序有以下錯誤:json

  咱們還發現當裝飾一個視圖函數時能夠正常運行,而裝飾兩個或兩個以上視圖函數則會報以上錯誤。flask

二、解決方式瀏覽器

  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")
複製代碼

2、flask中的路由

       Flask中的路由系統咱們已經見過了,而且一直在用,接下來學習幾個有關路由的使用方法。服務器

一、@app.route()裝飾器中的參數cookie

  1)methods : 當前 url 地址,容許訪問的請求方式,示例代碼以下session

  @app.route("/info", methods=["GET", "POST"])
  def stu_info():
    return "student info"

    注意:不寫methods參數時默認是隻容許GET請求,寫methods參數時要指定全部但願的容許的請求方式,默認方式會被methods覆蓋。

  2)endpoint:反向url地址,默認爲視圖函數名,url_for利用它進行反向解析,示例代碼以下

複製代碼
  from flask import Flask, url_for
  @app.route("/info", methods=["GET", "POST"], endpoint='s_info')   def stu_info():     print(url_for('s_info')) # /info     return "student info"
複製代碼

    注意:endpoint默認爲視圖函數名一致,不寫endpoint參數則使用視圖函數名反向解析,即url_for('stu_info'),有則使用endpoint定義的名稱進行反向解析。

  3)defaults:視圖函數的參數默認值,代碼以下

  @app.route("/info", methods=["GET", "POST"], defaults={"nid": 100})
  def stu_info(nid):
    print(nid)   # 100
    return "student info"

    注意:視圖函數必定要接收這個參數,且形參名稱必定要與defaults參數中的key一致。

  4)strict_slashes:url地址結尾符"/"的控制(False : 不管結尾"/"是否存在都可以訪問 , True : 結尾必須不能是"/",即與定義的路由徹底一致),示例代碼以下

複製代碼
  # 只能訪問 /info
  @app.route("/info", methods=["GET", "POST"], strict_slashes=True)
  def stu_info():
    return "student info"

  # 能夠訪問 /info    或者 /info/
  @app.route("/info", methods=["GET", "POST"], strict_slashes=False)
  def stu_info():
    return "student info"
複製代碼

  5)redirect_to:url地址永久重定向(對應狀態碼301)

複製代碼
  # 訪問地址 : /info 瀏覽器跳轉至 /infos
  @app.route("/info", redirect_to="/infos")
  def student_info():
    return "Hello info"

  @app.route("/infos")
  def student_infos():
    return "Hello infos"
複製代碼

    注意:在跳轉以前進行重定向,原url對應的視圖函數沒有執行,當即跳轉到新的url。且使用一次後對瀏覽器會有緩存。

  6)subdomain:子域名前綴 subdomian="car" 這樣寫能夠獲得 car.oldboy.com 前提是app.config["SERVER_NAME"] = "oldboy.com"

複製代碼
  app.config["SERVER_NAME"] = "oldboy.com"

  @app.route("/info",subdomain="car")
  def student_info():
    return "Hello info"
  # 訪問地址爲: car.oldboy.com/info
複製代碼

二、動態路由參數

複製代碼
  from flask import Flask, url_for
  # 訪問地址 : http://127.0.0.1:5000/info/1 能夠   # 訪問地址:http://127.0.0.1:5000/info/e 報錯   @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="s_info")   def student_info(nid):     print(nid, type(nid)) # 1 <class 'int'>     print(url_for("s_info", nid=nid)) # /info/1     print(url_for("s_info", nid=2)) # /info/2     return "Hello info"   # 訪問地址 : http://127.0.0.1:5000/info/1 能夠   # 訪問地址 : http://127.0.0.1:5000/info/e 能夠   @app.route("/info/<string:nid>", methods=["GET", "POST"], endpoint="s_info")   def student_info(nid):     print(nid, type(nid)) # 1 <class 'str'>     return "Hello info"   # 訪問地址 : http://127.0.0.1:5000/info/1 能夠   # 訪問地址 : http://127.0.0.1:5000/info/e 能夠   @app.route("/info/<nid>", methods=["GET", "POST"], endpoint="s_info")   def student_info(nid):     print(nid, type(nid)) # 1 <class 'str'>     return "Hello info"
複製代碼

  經過以上示例發現,<int:nid>就是在url後定義一個參數接收url中的參數,經過演示,還發現,當定義int類型時只能傳數字,當定義string類型時能夠接收數字和字符串,當不定義類型時,默認是string類型。

  注意:這種動態參數路由,在url_for的時候,必定要將動態參數名+參數值添加進去,不然會拋出參數錯誤的異常。

 

3、flask實例化配置

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                        主模塊所在的目錄的絕對路徑,默認項目目錄
複製代碼
 

4、flask對象配置

  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模式
 

5、初識flask藍圖(Blueprint)

  做爲初學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

 

6、flask特殊裝飾器

一、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錯誤時,會看到該函數返回的內容。

相關文章
相關標籤/搜索