Flask是一個基於 Python 開發而且依賴 jinja2 模板和 Werkzeug WSGI 服務的一個微型框架,對於 Werkzeug 本質是 Socket 服務端,其用於接收 http 請求並對請求進行預處理,而後觸發 Flask 框架,開發人員基於 Flask 框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助 jinja2 模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。html
pip3 install flask
from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simple @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': run_simple('localhost', 4000, hello)
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
from flask import Flask, request, render_template, redirect, session app = Flask(__name__, template_folder='templates', # 默認模板文件夾 static_folder='static', # 默認靜態文件文件夾 static_url_path='/static' # 默認靜態文件訪問路徑 ) app.config['DEBUG'] = True # 使用 session 必須定義 不然報錯 app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' # 默認只支持 GET 請求 @app.route('/login', methods=['GET', "POST"]) def login(): if request.method == 'GET': # 渲染模板 默認對應templates 文件夾 return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') if user == 'zze' and pwd == '666': session['user'] = user # 重定向 return redirect('/index') return render_template('login.html', msg='用戶名或密碼錯誤!') @app.route('/index') def index(): user = session['user'] if not user: return redirect('/login') return render_template('index.html') if __name__ == '__main__': app.run()
app.config 本質上實際上是一個字典,因此能夠經過以下方式進行配置:python
from flask import Flask app = Flask(__name__) app.config['DEBUG'] = True
固然,Flask 也提供了單文件配置的方式,以下:django
class Dev: DEBUG = True
from flask import Flask app = Flask(__name__) app.config.from_object('settings.Dev')
app.config.from_pyfile("settings.py") # 如: # settings.py # DEBUG = True
app.config.from_envvar("環境變量名稱") # 環境變量的值爲python文件名稱名稱,內部調用from_pyfile方法
app.config.from_json("json文件名稱") # json文件名稱,必須是json格式,由於內部會執行json.loads
app.config.from_mapping({'DEBUG':True}) # 字典格式
{ 'DEBUG': get_debug_flag(default=False), 是否開啓Debug模式 'TESTING': False, 是否開啓測試模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': None, 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, }
@app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') @app.route('/login', methods=['GET', 'POST'])
經常使用路由系統有以上五種,全部的路由系統都是基於一下對應關係來處理:json
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
能夠經過 endpoint 和 url_for 反向生成:flask
from flask import Flask, url_for app = Flask(__name__) @app.route('/index', endpoint='i') def index(): return url_for('i') # endpoint 值未指定時,默認爲方法名 if __name__ == '__main__': app.run()
Flask 使用的是 Jinjia2 模板,因此其語法和在 Django 中使用時無差異。瀏覽器
Markup 等價 django 的 mark_safe。cookie
{% macro input(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {{ input('n1') }}
request 內置屬性: request.method # 方法 request.args # GET 請求時的參數 request.form # POST 請求時的參數 request.values # 全部參數 request.cookies # cookie 內容 request.headers # 請求頭信息 request.path # 請求路徑 request.full_path # 請求全路徑 路徑相關: request.script_root request.url request.base_url request.url_root request.host_url request.host request.files # 上傳文件接收
# 保存文件 obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename))
# 返回字符串 return "字符串" # 返回渲染模板 return render_template('html模板路徑',**{}) # 重定向 return redirect('/index.html')
當請求剛到來時,flask 讀取 cookie 中 session 對應的值,並將該值解密並反序列化成字典,放入內存以便視圖函數使用。session
當請求結束時,flask 會讀取內存中字典的值,進行序列化和加密,再寫入到 cookie 中。app
從 flask 的入口 app.run 方法看起,框架
1 def run(self, host=None, port=None, debug=None, 2 load_dotenv=True, **options): 3 if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': 4 from .debughelpers import explain_ignored_app_run 5 explain_ignored_app_run() 6 return 7 8 if get_load_dotenv(load_dotenv): 9 cli.load_dotenv() 10 11 # if set, let env vars override previous values 12 if 'FLASK_ENV' in os.environ: 13 self.env = get_env() 14 self.debug = get_debug_flag() 15 elif 'FLASK_DEBUG' in os.environ: 16 self.debug = get_debug_flag() 17 18 if debug is not None: 19 self.debug = bool(debug) 20 21 _host = '127.0.0.1' 22 _port = 5000 23 server_name = self.config.get('SERVER_NAME') 24 sn_host, sn_port = None, None 25 26 if server_name: 27 sn_host, _, sn_port = server_name.partition(':') 28 29 host = host or sn_host or _host 30 port = int(port or sn_port or _port) 31 32 options.setdefault('use_reloader', self.debug) 33 options.setdefault('use_debugger', self.debug) 34 options.setdefault('threaded', True) 35 36 cli.show_server_banner(self.env, self.debug, self.name, False) 37 38 from werkzeug.serving import run_simple 39 40 try: 41 run_simple(host, port, self, **options) 42 finally: 43 self._got_first_request = False
直接看到 41 行,這裏的 run_simple 方法實際上就是 werkzeug.serving.run_simple ,而傳入的第三個參數 self 實際上就是指的 app 自己。咱們先要了解的是,在上面的werkzeug的簡單使用中,傳入的是一個方法,而且這個方法會在請求到來時被執行。而在這裏傳入一個對象,加 () 執行一個對象時其實是執行這個對象的 __call__ 方法,查看 app.__call__ 方法:
1 def __call__(self, environ, start_response): 2 return self.wsgi_app(environ, start_response)
看到這裏咱們能夠肯定,後續整個程序的執行就是依賴這第 2 行的觸發,而咱們只要在第二行執行以前定義的處理就至關於中間件的前置處理,在它以後的處理就至關於後置處理了。因此能夠經過以下方法實現中間件的功能:
from flask import Flask
app = Flask(__name__) class MiddleWare: def __init__(self, wsgi_app): self.wsgi_app = wsgi_app def __call__(self, *args, **kwargs): print('前置處理') obj = self.wsgi_app(*args, **kwargs) print('後置處理') if __name__ == "__main__": app.wsgi_app = MiddleWare(app.wsgi_app) app.run(port=9999)
flash 就是用來存儲只使用一次的數據,相似於將數據放入列表,而後經過 pop 方法取出。
flash(message, category='message') get_flashed_messages(with_categories=False, category_filter=[])
from flask import Flask, flash, get_flashed_messages app = Flask(__name__) @app.route('/page1') def page1(): flash('臨時數據') return 'page1' @app.route('/page2') def page2(): get_flashed_messages() return 'page2' if __name__ == '__main__': app.run()
1 def flash(message, category='message'): 2 flashes = session.get('_flashes', []) 3 flashes.append((category, message)) 4 session['_flashes'] = flashes 5 message_flashed.send(current_app._get_current_object(), 6 message=message, category=category)
能夠看到它其實就是把 message 和 category 以元組的形式存儲在 session 中鍵爲 _flashes 的值中。
再看 get_flashed_messages 方法:1 def get_flashed_messages(with_categories=False, category_filter=[]): 2 flashes = _request_ctx_stack.top.flashes 3 if flashes is None: 4 _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \ 5 if '_flashes' in session else [] 6 if category_filter: 7 flashes = list(filter(lambda f: f[0] in category_filter, flashes)) 8 if not with_categories: 9 return [x[1] for x in flashes] 10 return flashes
從第 4 行能夠看到每次獲取 flash 數據時,就是從 session 中將以前存入的鍵爲 _flashes 的元素 pop 出來,而後進行過濾返回對應 category 內容。
from flask import Flask, flash, get_flashed_messages app = Flask(__name__) app.secret_key = '123123' @app.before_request def before_request1(): print('before_request1') @app.before_request def before_request2(): print('before_request2') @app.after_request def after_request1(response): print('after_request1') return response @app.after_request def after_request2(response): print('after_request2') return response @app.route('/index') def index(): print('index') return 'index' if __name__ == '__main__': app.run() # 執行順序以下: # before_request1 # before_request2 # index # after_request2 # after_request1
from flask import Flask, flash, get_flashed_messages, render_template app = Flask(__name__) app.secret_key = '123123' @app.template_filter() def add_filter(a, b): return a + b @app.route('/index') def index(): print('index') return render_template('index.html') if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{1|add_filter(2)}} </body> </html>
from flask import Flask, flash, get_flashed_messages, render_template app = Flask(__name__) app.secret_key = '123123' @app.template_global() def add_template_func(a, b): return a + b @app.route('/index') def index(): print('index') return render_template('index.html') if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{add_template_func(1,2)}} </body> </html>
@app.errorhandler(404) def page_not_found(error): return render_template('404.html'),404
@app.before_first_request def application_start(): # 同 before_request,但僅在第一次請求時執行一次。 pass