flask框架搭的web項目
html應當寫在templates文档夾裡面
css文档還有靜態資源(例如圖片)應該放在static文档夾裡面,直接訪問 localhost:80/static/a.jpg
應用程序根目錄是根據初始化app=Flask(__name__)的時候的代碼在哪就決定了哪裡是根目錄
更改flask的默認設置靜態資源位置:
關鍵詞: static_folder
app = Flask(__name__, static_folder='hehe') # http://127.0.0.1:5000/hehe/haha/a.png app = Flask(__name__, static_folder="hehe/haha") # http://127.0.0.1:5000/haha/a.png
關鍵詞: static_url_path:
前耑訪問資源文档的前綴目錄。默認是/static,就是前耑必須這樣訪問:<img src="/static/img/mylogo.jpg" />
我們改成 '',就可以這樣訪問了:<img src="/img/mylogo.jpg" />。就達到前耑從根目錄訪問的目的了。
app = Flask(__name__, static_folder="hehe",static_url_path="") # http://127.0.0.1:5000/haha/a.png
關鍵詞: url_for()
這個函數有很多的功能,可根據你定義的函數名獲取到@app.route()這個路由語句傳進來的url參數(動態的帶尖括號<>的無效)
@app.route('/test') def tt(): return url_for("haha") # 頁面會打出 /someurl @app.route("/someurl") def haha(): return url_for("tt") # 頁面會打出 /test
關鍵詞: redirect
功能就是跳轉到指定的url,大部分情況下,我們都是用return關鍵字阻止下文的繼續執行, 也可以和url_for方法一起使用,例如:
@app.route('/') def hello_world(): return 'Hello World' @app.route('/<name>/') def hello(name): if name == 'Harp': return 'Hello %s' % name else: return redirect(url_for('hello_world'))
在hello這個視圖函數中,如果url傳入的參數是Harp(即請求的網址是http://127.0.0.1:5000/Harp/),則返回'Hello Harp',其他情況則重定向到hello_world這個視圖函數對應的網址'/'。
如何獲取input標簽的用戶名和密碼?
方式一:request.form['username']
<head> <meta charset="UTF-8"> <title>‹/title> </head> <body> <h1>HTTP 方法:{{method }J</h1> <form method="post"> <div> <input type="text" name=iusername" placeholder="User name"/> </div> <div> <input type="password" name="password" placeholder="Password" /> </div> <input type="submit"/> </form> </body> </html>
下面指定的username和password是和上面的form中的name是一致的
Capp.route('/login', methods=['GET', 'POST']) def login(): if request.method=='POST': username=request.form['username'] password=request.form['password'] return render_template('login.html', method=request.method)
方式二:request.args['username']
如果要通過url來傳送數據 , https://localhost:5000/login?username=jenrey 就要用下面這種方式獲取參數值
@app.route('/login', methods=['GET', 'POST') def login(): if request.method=='POST': username=request.form['username'] password=request. form['password'] else: username = request. args['username'] return render_template('login.html', method=...
關鍵字: render_template
重要, 作用:render_template不僅能渲染靜態的html文档,也能傳遞參數給html。在templates文档夾建立一個html文档,内容隨便寫:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Index</title> </head> <body> <h1>This is index page</h1> </body> </html>
需要在項目最開始的代碼中找到Flask的主變量, 在裡面多傳一個template_folder參數, 而且要import關鍵字來引入render_template, 例如:
from flask import Flask, url_for, redirect, render_template app = Flask(__name__, template_folder="template")
這段代碼中的template對應項目根目錄中的template, 然後把你想要渲染的視圖文档塞到template文档夾裡。
談如何寫入cookie:
from flask import Flask,render_template,request,redirect,url_for,make_response
... 中間代碼省略
@app.route('/') def index(): response = make_response(render_template(index.html, title='Welcome')) response.set_cookie('username', '') return response
DIY異常頁面, 關鍵字: @app.errorhandler(400)
不需要指定路由地址,只需指定狀態碼,這樣出現錯誤的時候,他就會自己響應到所對應的頁面了。
@app.errorhandler(404) def page_not_found(error): return render_template('404.html'), 404
接下來, 自己編寫一個名爲404.html的報錯頁面
一條命令生成依賴引用文档:
C:\Users\Desktop\testflask>pip freeze > requirements.txt
這個cmd命令執行完畢會産生一個文档,他會把我們用到的包列入到這個叫requirements.txt的文本文档。
requirements這個文档名其實是python約定俗成的用法,當時我們使用這個文档名,pycharm會自動幫我們識別我們裝了哪些包沒裝哪些包
安裝依賴引用文档的包的命令
pip install -r requirement.txt
關鍵字: flask_script
先安裝模塊
pip install flask_script
flask_script 作用:可以通過命令行的形式來操作Flask,例如通過命令跑一個開發版本的服務器、設置數據庫、定時任務等
在項目代碼中導入包:
from flask.ext.script import Manager
或
from flask_script import Manager
直接把flask對象app傳入進去。整體代碼如下:
from flask import Flask from flask_script import Manager app = Flask(__name__) manager = Manager(app) @app.route('/<a>') def hello(a): return "hello" + str(a) if __name__ == '__main__': # app.run(debug=True) manager.run()
用命令啓動網站:
python manage.py runserver
然後在瀏覽器裡輸入
localhost:5000/a
可以訪問到自己寫的網站
flask-script官網:https://flask-script.readthedocs.org/en/latest/
具體的@manager.command用法看下面
關鍵字: livereload:
先安裝模塊:
pip install livereload
代碼中引入的方式:
from livereload import Server
代碼案例:
@manager.command Idef dev(): from livereload import Server live_server= Server (app.wsgi_app) live_server.watch('**/*.*') live_server. serve (open_url=True) if __name__== '__main__': manager.run()
用@manager.command來,上面就傳入了dev的參數
使用@app.template_filter() 把filter注冊到模板
我們只要在函數上聲明這個裝飾器,就可以把這個filter注冊到模板
先說說怎麽自定義Markdown過濾器, 上文提到過requirements.txt這個文本文档, 假設你已經用過requirements這個包也實現了這個依賴包配置文档的導出, 你需要找到這個文档所在的路徑, 然後在同路徑裡面輸入下方的命令來定制Markdown過濾器的版本:
pip install -r requirements.txt
如果你對windows的路徑什麽的雲裡霧裡? 舉個例子, 如果你知道requirements.txt是在C:\somefolders\balabala\下面, 那它的路徑全稱應該是:
C:\somefolders\balabala\requirements.txt
舉一反三, 那麽上述的命令就變成了
pip install -r C:\somefolders\balabala\requirements.txt
發現有前耑的哥們對這個特別迷糊,一定要把文档路徑這個套路搞清楚了。在mac系統裡其實套路也一樣, 只不過反斜杠變成了正斜杠, 捺變成了撇。
代碼裡這樣寫:
@app.route('/') def index(): return render_template('index.html', title="<h1>Hello world</hi>", body="## Header2") @app.template_filter('md") def markdown_to_html(txt) : from markdown import markdown return markdown(txt)
模板裡這樣寫(部門html代碼省略)
<body> {{ title|safe }} {{ body|md|safe }} </body>
關鍵字: @app.context_processor上下文處理器
直接把方法注冊到模板中。上下文處理器本身會返回一個字典的函數。其裝飾的函數返回的内容對所有html模板都起作用。
@app.context_processor def inject_methods(): return dict(read_md=read_md)
在代碼中的實現, 省略部分html代碼:
<body> {{ title|safe }} {{ body|md|safe }} {{ read_md('http_methods.md')|md|safe }} </body>
如果你想看看這裡面都是啥意思, 請你訪問一下這個路徑看看具體列印出啥來了?
關鍵字: @app.template_test 自定義測試函數
@app.template_test('current_link') def is_current_link(link) return link['href'] == request.path
在視圖模板裡這麽寫:
{% for link in links %} {% if_not loop.first %} | {% endif %} {% if link is current_link %} <a href="{{ link.href }}">{{ link.label }}</a> {% endif %} {% endfor %}
關鍵字: flash 消息閃現
(如果你會前耑, 建議你還是用layer.js在模板裡自己搞)
寫入代碼導入flash包
from flask import Jsonify, request, Current_app, url_for, render_template, flash
代碼裡直接call函數
flash('嗨你好啊')
在視圖模板文档裡找個地方寫以下代碼看效果:
{% set message = get_flashed_messages() %} {% messages %}
需要在配置文档中設置 SECRET_KEY 字符串, 可以隨便寫這個秘鈅,能保证數據的安全,因爲flask的session是寫在客戶耑的,所以設置完SECRET_KEY後就可以通過系統自己的算法來進行驗证數據安全的問題了。
SECRET_KEY = 'xxxxxxxxxxxx'
這裡看起來是個列表的形式,所以我們可以多次使用flash()
flash('xxxxxxxxxxxx') flash('yyyyyyyyyyyy')
消息閃現的消息分類:
flash('我要報錯', category='error') flash('我要警告', category='warning')
然後視圖裡你可以這麽寫
{% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %} <!-- 中間省略代碼 --> {% endif %} {% endwith %}
with和endwith是限制變量作用域的,errors只會在其之間有效果。原來我們不加with,變量的作用域是在block中的,加了之後就變成with中了。
知識點: 開啓DEBUG調試模式
有多種方法來開啓debug模式:
1. 在app.run()中添加參數,變爲app.run(debug=True);
2. 在run之前增加app.debug = True;
3. 新建config.py文档,在config文档中添加DEBUG = True,然後在程序中引入app.config.from_object(config);
4. 在run之前增加app.config['DEBUG'] = True;
建議使用第3種方式,其中還可以寫入以下信息
SECRET_KEY
SQLALCHEMY_DB
APP_ROOT
send_static_file:
我們可以使用Flask對象app的send_static_file方法,使視圖函數返回一個靜態的html文档,但現在我們不使用這種方法,而是使用flask的render_template函數,它功能更強大。
從flask中導入render_template,整體代碼如下:
from flask import Flask, render_template import config app = Flask(__name__) app.config.from_object(config) @app.route('/') def index(): return render_template('index.html') if __name__ == '__main__': app.run()
render_template函數會自動在templates文档夾中找到對應的html,因此我們不用寫完整的html文档路徑。用瀏覽器訪問'localhost:5000/'這個地址,顯示index.html的内容: This is index page
使一個html模板根據參數的不同顯示不同的内容,這是因爲flask使用了jinja2這個模板引擎。要使用模板,在render_template參數中以key=value形式傳入變量,在html中使用{{key}}來顯示傳入的變量,例如:
python視圖處理代碼:
# 視圖函數 @app.route('/') def index(): return render_template('index.html', contents='This is index page')
html代碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Index</title> </head> <body> <h1>{{ contents }}</h1> </body> </html>
瀏覽器顯示的結果與上文是一樣的。我們還可以直接把一個類的實例傳遞過去,並在模板中訪問類的屬性,例如假設一個類對象obj有a和b屬性,關鍵部分的代碼如下:
視圖函數中
return render_template('index.html', object=obj) # ...
html模板中
<p>a: {{ object.a }}</p> <p>b: {{ object.b }}</p>
傳入一個字典也可以,並且在模板中既可以用dict[key],也可以用dict.key。
關鍵字: ORM 和 SQLAlchemy
重頭戲來了, 以MySQL爲例,平時我們會用mysqldb(python 2)或者pymysql(python 3)去操作MySQL數據庫,但這種方法也是需要自己編寫SQL語句的。現在我們有了ORM模型,簡單來說,ORM是把數據庫中的表抽象成模型,表的列名對應模型的屬性,這樣我們可以調用類的屬性或方法去獲得數據庫中的數據。例如假設MySQL數據庫中有一張表名爲table1,使用SQL語句例如:
SELECT * FROM table1 WHERE id=1
來獲取id爲1的數據,如果將表table1映射成ORM模型Table,那麽可以直接使用Table.query.filter(id=1),這樣操作簡單了很多,也很利於理解。SQLAlchemy就是一個這樣的ORM,我們可以直接導入一個flask_sqlalchemy包來使用
在配置文档config.py中填寫好數據庫的連接信息:
HOST = "127.0.0.1" PORT = "3306" DB = "harp" USER = "root" PASS = "Your Password" CHARSET = "utf8" DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}?charset={}".format(USER, PASS, HOST, PORT, DB, CHARSET) SQLALCHEMY_DATABASE_URI = DB_URI
SQLAlchemy依賴mysqldb或者pymysql去連接數據庫和執行SQL語句,因爲我們用的是python 3,所以需要在配置信息中指明使用pymysql,如果是python 2可以省略,默認是使用mysqldb。
數據庫建表
直接上代碼
from flask_sqlalchemy import SQLAlchemy from datetime import datetime import config app = Flask(__name__) app.config.from_object(config) db = SQLAlchemy(app) class Users(db.Model): __tablename__ = 'users_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(32), nullable=False) password = db.Column(db.String(100), nullable=False) register_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) db.create_all()
__tablename__這個屬性就是建表後,數據庫生成的表名。primary_key=True說明該字段爲主鍵,autoincrement=True代表自增長,nullable決定是否可爲空,default代表默認值。最後用db.create_all()來實現創建。
進入數據庫命令行,輸入desc user_info;這個指令, 我們發現表已經建立好了,其結構圖如下:
mysql> desc users_info;
Field | Type | Null | Key | Default | Extra |
id | int(11) | NO | PRI | NULL | auto_increment |
username | varchar(32) | NO | NULL | ||
password | varchar(100) | NO | NULL | ||
register_time | datetime | NO | NULL |
在數據庫裡插入數據
上代碼
@app.route('/') def index(): user = Users(username='Sharp', password='123456') db.session.add(user) db.session.commit() return render_template('home.html')
代碼實例化一個Users的對象user,傳入username和password,使用db.session.add(user)將其加入到數據庫的session(可以理解爲事務)中,然後使用db.session.commit()提交。我們運行程序,然後用瀏覽器訪問,瀏覽器正常顯示了結果,這時再看一眼數據庫,發現這條數據已經寫入到了數據庫:
mysql> select * from users_info;
id | username | password | register_time |
1 | Sharp | 123456 | 2022-10-13 10:08:22 |
數據庫的查詢、修改數據
代碼如下:
@app.route('/')
def index(): user = Users.query.filter(Users.id == 1).first() #查找 print(user.username) user.username = 'Harp1207' #修改 db.session.commit() #修改後需提交 print(user.username) return render_template('home.html')
外鍵關聯
users_info表(Users模型)代碼如下:
class Users(db.Model): __tablename__ = 'users_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(32), nullable=False) password = db.Column(db.String(100), nullable=False) register_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) # 我們新增了一個avatar_path字段來存用戶頭像圖片文档的路徑 avatar_path = db.Column(db.String(256), nullable=False, default='images/doraemon.jpg')
questions_info表(Questions模型)代碼如下:
class Questions(db.Model): __tablename__ = 'questions_info' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(100), nullable=False) content = db.Column(db.TEXT, nullable=False) author_id = db.Column(db.Integer, db.ForeignKey('users_info.id')) create_time = db.Column(db.DateTime, nullable=False, default=datetime.now()) author = db.relationship('Users', backref=db.backref('questions', order_by=create_time.desc()))
這個表存儲所有問題的標題、内容、創建時間、作者ID,作者ID通過外鍵與用戶表的ID關聯,方式也很簡單,在db.Column中用db.ForeignKey('users_info.id')作爲參數即可。
再看最後一條語句:
author = db.relationship('Users', backref=db.backref('questions', order_by=create_time.desc()))
db.relationship會自動找到兩個表的外鍵,建立Questions和Users的關係,此時對於任意一個Questions對象question,通過question.author就可獲得這個question的作者對應的Users對象,例如獲取id爲1的問題的作者姓名:
question = Questions.query.filter(Questions.id == 1).first() author_name = question.author.username
db.relationship的第二個參數backref=db.backref('questions', order_by=create_time.desc())則建立了一個反向引用,這樣我們不僅可以使用question.author,還可以使用author.questions獲得一個作者所有的問題,並通過order_by=create_time.desc()按創建時間倒序排列(網頁的内容按時間倒序排列),返回的是一個Questions對象的列表,可以遍歷它獲取每個對象,如獲取作者Harp的所有問題的title:
author = Users.query.filter(Users.username == 'Harp').first() for question in author.questions: print(question.title)
flask-migrate 數據庫遷移(用途:更新表結構)
我們增加了兩個模型Questions和Comments,並爲Users增加了avatar_path這個字段,然後通過這段代碼更新到數據庫:
with app.test_request_context(): db.drop_all() db.create_all()
因爲當使用過db.create_all()之後,再次直接使用db.create_all(),對模型的修改並不會更新到數據庫,我們要使用db.drop_all()先把數據庫中所有的表先刪除掉,然後再db.create_all()一次。聽上去是不是很麻煩?更糟糕的是,原先數據庫的的數據也就沒有了。所以我們不用這種簡單粗暴的方式去更新數據庫結構,而是借助flask-migrate這個專門用於遷移數據庫的工具,它可以在保留數據庫原始數據的情況下,完成模型的更新。此外,我們還將結合flask-script一起使用,簡單來說flask-script讓我們可以使用命令行去完成數據庫遷移的操作。
在項目主文档夾下新建一個manage.py,代碼如下:
from flask_script import Manager from flask_migrate import Migrate, MigrateCommand from HarpQA import app, db from models import Users, Questions, Comments manager = Manager(app) migrate = Migrate(app, db) manager.add_command('db', MigrateCommand) if __name__ == '__main__': manager.run()
首先導入相關的類,注意模型要全部導入過來,即使代碼中並沒有顯式地使用它們。然後傳入app或db來構建Manager和Migrate兩個類的實例,最後將MigrateCommand的命令加入到manager中。
此時我們假設要更新模型的結構,在models.py的User模型結尾添加一行代碼test = db.Column(db.Integer),然後點擊PyCharm下方的Terminal,自動進入到了虛擬環境的命令行中,輸入python manage.py db init來初始化,這一步主要是建立數據庫遷移相關的文档和文档夾,只是在第一次需要使用。接著依次使用python manage.py db migrate和python manage.py db upgrade,待運行完成,查看users_infor表的結構,結果如下:
Field | Type | Null | Key | Default | Extra |
id | int(11) | NO | PRI | NULL | auto_increment |
username | varchar(32) | NO | NULL | ||
password | varchar(100) | NO | NULL | ||
register_time | datetime | NO | NULL | ||
avatar_path | varchar(256) | NO | NULL | ||
test | int(11) | YES | NULL |
可以看到test字段已經添加到表中了。
werkzeug.security 哈希與字符串的加密與解密
werkzeug.security中的generate_password_hash這個函數,功能是將字符串變成hash值。
password=generate_password_hash(password1)
werkzeug.security的check_password_hash方法,它能驗证哈希值是否與原始的密碼是匹配的
check_password_hash(加密後的哈希, 需要對比的非哈希密碼)
常用判斷語句 request.method == 'GET'
爲了方便說明, 直接上代碼
@app.route('/question/', methods=['GET', 'POST'])
def question(): if request.method == 'GET': return render_template('question.html') else: question_title = request.form.get('question_title') question_desc = request.form.get('question_desc') author_id = Users.query.filter(Users.username == session.get('username')).first().id new_question = Questions(title=question_title, content=question_desc, author_id=author_id) db.session.add(new_question) db.session.commit() return redirect(url_for('home'))
常用鉤子函數 @app.before_request
函數裡面有before_request,看其名字就很好理解,是在request之前會自動運行的,我們在每次請求之前(或者說每次運行視圖函數之前)。
例如通過鉤子函數來得到當期登錄用戶的User對象(而不是僅僅是session中的username),然後在需要的地方使用它,代碼如下:
@app.before_request def my_before_request(): username = session.get('username') if username: g.user = Users.query.filter(Users.username == username).first()
這個鉤子函數,從session中獲取當前登陸的username,如果獲取到了,再去檢索Users模型,把返回的user對象存入到g對象中,在視圖函數中我們就可以直接使用這個user對象的id/register_time等字段了。此時前面的視圖函數中的
author_id = Users.query.filter(Users.username == session.get('username')).first().id
可以修改成
author_id = g.user.id
g對象不能跨請求使用,因此在上下文管理器中用的是session,爲什麽這裡又用了g對象呢?原因是現在有了鉤子函數,每次請求都會執行鉤子函數,向g對象中寫入user,所以上下文管理器一直都能從g對象中取到user,不管這個g對象是屬於哪次請求的。
獲取上傳的文档的方法 request.files[name]
與獲取POST數據類似(上傳文档其實也是使用POST方法),在flask中使用request.files[name]獲取上傳的文档,其中name爲對應input控件的name值(name="avatar_upload"),然後使用文档的save方法即可保存。例如:
@app.route('/user/avatar/', methods=['GET', 'POST']) def avatar(): if request.method == 'GET': return render_template('avatar.html') else: file = request.files['avatar_upload'] path = "D:\\Flask\\HarpQA\\static\\" file.save(path + file.filename) return 'Saved'
注意save方法要加上具體的路徑,默認不會保存到py文档所在的路徑,而是系統的根目錄,此時會提示Permission denied。
set_cookie 設置cookie
make_response方法生成一個response對象(這個對象有set_cookie方法,這也是Flask設置cookie的常槼方法),並爲其設置cookie,set_cookie第一個參數'sid'是key,第二個參數是value(session id),之後返回response對象。當請求是GET時候,首先就會使用request.cookies.get('sid')去獲取cookie中的session id
make_response 方法
make_response(),相當於Django框架中的HttpResponse。
返回内容的寫法
from flask import make_response @app.route('/makeresponse/') def make_response_function(): response = make_response('<h2>羞羞噠</h2>') return response, 404
返回頁面的寫法
from flask import make_response @app.route('/makeresponse/') def make_response_function(): temp = render_template('hello.html') response = make_response(temp) return response
注意:make_response 想要返回頁面,不能直接寫做:make_response('hello.html'),必須用render_template('hello.html')形式。
返回狀態碼
方式一:在make_response()中傳入狀態碼
from flask import make_response @app.route('/makeresponse/') def make_response_function(): temp = render_template('hello.html') response = make_response(temp, 200) return response
方式二:直接return狀態碼
from flask import make_response @app.route('/makeresponse/') def make_response_function(): temp = render_template('hello.html') response = make_response(temp) return response, 200
redirect 跳轉
flask中的 redirect 相當於 Django框架的 HttpResponseRedirect。
1. 參數是url形式
from flask import redirect @app.route('/redirect/') def make_redirect(): return redirect('/hello/index/')
2. 參數是 name.name 形式
url_for 相當於reverse,name.name 相當於Django框架的namespace:name,第一個name是初始化藍圖時的參數名,第二個name是函數名
blue = Blueprint('first', __name__)
@blue.route('/index/')
def index():
return render_template('hello.html')
from flask import redirect @blue.route('/redirect/') def make_redirect(): return redirect(url_for('first.index'))
request的屬性
#代碼示例,僅僅是爲了測試request的屬性值
@app.route('/login', methods = ['GET','POST']) def login(): if request.method == 'POST': if request.form['username'] == request.form['password']: return 'TRUE' else: # 當form中的兩個字段内容不一致時,返回我們所需要的測試信息 return str(request.headers) # 需要替換的部分 else: return render_template('login.html')
1、method:請求的方法
return request.method #POST
2、form:返回form的内容
return json.dumps(request.form) #{"username": "123", "password": "1234"}
3、args和values:args返回請求中的參數,values返回請求中的參數和form
return json.dumps(request.args) #url:http://192.168.1.183:5000/login?a=1&b=2、返回值:{"a": "1", "b": "2"}
return str(request.values) #CombinedMultiDict([ImmutableMultiDict([('a', '1'), ('b', '2')]), ImmutableMultiDict([('username', '123'), ('password', '1234')])])
4、cookies:cookies信息
return json.dumps(request.cookies) #cookies信息
5、headers:請求headers信息,返回的結果是個list
return str(request.headers) #headers信息
request.headers.get('User-Agent') #獲取User-Agent信息
6、url、path、script_root、base_url、url_root:看結果比較直觀
return 'url: %s , script_root: %s , path: %s , base_url: %s , url_root : %s' % (request.url,request.script_root, request.path,request.base_url,request.url_root)
#url: http://192.168.1.183:5000/testrequest?a&b , script_root: , path: /testrequest , base_url: http://192.168.1.183:5000/testrequest , url_root : http://192.168.1.183:5000/
7、date、files:date是請求的數據,files隨請求上傳的文档
@app.route('/upload',methods=['GET','POST']) def upload(): if request.method == 'POST': f = request.files['file'] filename = secure_filename(f.filename) #f.save(os.path.join('app/static',filename)) f.save('app/static/'+str(filename)) return 'ok' else: return render_template('upload.html')
html部分
<!DOCTYPE html> <html> <body> <form action="upload" method="post" enctype="multipart/form-data"> <input type="file" name="file" /><br /> <input type="submit" value="Upload" /> </form> </body> </html>
jsonify flask提供的json格式數據處理方法
python的flask框架爲用戶提供了直接返回包含json格式數據響應的方法,即jsonify
在flask中使用jsonify和json.dumps的區別: https://blog.csdn.net/JENREY/article/details/86509593
g對象
1. 在flask中,有一個專門用來存儲用戶信息的g對象,g的全稱的爲global。
2. g對象在一次請求中的所有的代碼的地方,都是可以使用的。
flask之g對象: https://www.wkwkk.com/articles/d9d1e3ea47c6f398.html
Flask-HTTPAuth 擴展 HTTP認证
終耑下運行命令來安裝:
pip install flask-httpauth
Flask-HTTPAuth提供了幾種不同的Auth方法,比如HTTPBasicAuth,HTTPTokenAuth,MultiAuth和HTTPDigestAuth。
HTTPBasicAuth:基礎認证
創建擴展對象實例
from flask import Flask from flask_httpauth import HTTPBasicAuth app = Flask(__name__) auth = HTTPBasicAuth()
注意,初始化實例時不需要傳入app對象,也不需要調用”auth.init_app(app)”注入應用對象。
案例:用戶名及密碼驗证
我們所要做的,就是實現一個根據用戶名獲取密碼的回調函數:
@auth.get_password 的使用(明文密碼有效)
users = [ {'username': 'Tom', 'password': '111111'}, {'username': 'Michael', 'password': '123456'} ] @auth.get_password def get_password(username): for user in users: if user['username'] == username: return user['password'] return None
回調函數”get_password()”由裝飾器”@auth.get_password”修飾。在函數裡,我們根據傳入的用戶名,返回其密碼;如果用戶不存在,則返回空。
@auth.login_required 的使用
接下來,我們就可以在任一視圖函數上,加上”@auth.login_required”裝飾器,來表示該視圖需要認证:
@app.route('/') @auth.login_required def index(): return "Hello, %s!" % auth.username()
啓動該應用,當你在瀏覽器裡打開”http://localhost:5000/”,你會發現瀏覽器跳出了瀏覽器原生味道的登錄框,輸入正確的用戶名密碼(比如上例中的Tom:111111)後,”Hello Tom!”的字樣才會顯示出來。
進入瀏覽器調試,發現認证並沒有啓用Cookie,而是在請求頭中加上了加密後的認证字段:
Authorization: Basic TWljaGFlbDoxMjM0NTY=
這就是”HTTPBasicAuth”認证的功能,你也可以用Curl命令來測試:
curl -u Tom:111111 -i -X GET http://localhost:5000/
@auth.verify_password 的使用(非明文密碼有效)
上例中”@auth.get_password”回調只對明文的密碼有效,但是大部分情況,我們的密碼都是經過加密後才保存的,這時候,我們要使用另一個回調函數”@auth.verify_password”。在演示代碼之前,先要介紹Werkzeug庫裡提供的兩個方法:
generate_password_hash: 對於給定的字符串,生成其加鹽的哈希值
check_password_hash: 驗证傳入的哈希值及明文字符串是否相符
這兩個方法都在”werkzeug.security”包下。現在,我們要利用這兩個方法,來實現加密後的用戶名密碼驗证:
from werkzeug.security import generate_password_hash, check_password_hash users = [ {'username': 'Tom', 'password': generate_password_hash('111111')}, {'username': 'Michael', 'password': generate_password_hash('123456')} ] @auth.verify_password def verify_password(username, password): for user in users: if user['username'] == username: if check_password_hash(user['password'], password): return True return False
在”@auth.verify_password”所修飾的回調函數裡,我們驗证傳入的用戶名密碼,如果正確的話返回True,否則就返回False。
錯誤處理
在之前的例子中,如果未認证成功,服務耑會返回401狀態碼及”Unauthorized Access”文本信息。你可以重寫錯誤處理方法,並用”@auth.error_handler”裝飾器來修飾它:
from flask import make_response, jsonify @auth.error_handler def unauthorized(): return make_response(jsonify({'error': 'Unauthorized access'}), 401)
有了上面的”unauthorized()”方法後,如果認证未成功,服務耑返回401狀態碼,並返回JSON信息”{‘error’: ‘Unauthorized access’}”。
HTTPTokenAuth:Token認证
在對HTTP形式的API發請求時,大部分情況我們不是通過用戶名密碼做驗证,而是通過一個令牌,也就是Token來做驗证。此時,我們就要請出Flask-HTTPAuth擴展中的HTTPTokenAuth對象。
同HTTPBasicAuth類似,它也提供”login_required”裝飾器來認证視圖函數,”error_handler”裝飾器來處理錯誤。
@auth.verify_token 的使用
區別是,它沒有”verify_password”裝飾器,相應的,它提供了”verify_token”裝飾器來驗证令牌。我們來看下代碼,爲了簡化,我們將Token與用戶的關係保存在一個字典中:
from flask import Flask, g from flask_httpauth import HTTPTokenAuth app = Flask(__name__) auth = HTTPTokenAuth(scheme='Bearer') tokens = { "secret-token-1": "John", "secret-token-2": "Susan" } @auth.verify_token def verify_token(token): g.user = None if token in tokens: g.user = tokens[token] return True return False @app.route('/') @auth.login_required def index(): return "Hello, %s!" % g.user
可以看到,在”verify_token()”方法裡,我們驗证傳入的Token是否合法,是的話返回True,否則返回False。另外,我們通過Token獲取了用戶信息,並保存在全局變量g中,這樣視圖中可以獲取它。注意,在第一節的例子中,我們使用了”auth.username()”來獲取用戶名,但這裡不支持。
初始化HTTPTokenAuth對象時,我們傳入了”scheme=’Bearer'”。這個scheme,就是我們在發送請求時,在HTTP頭”Authorization”中要用的scheme字段。
啓動上面的代碼,並用Curl命令來測試它:
curl -X GET -H "Authorization: Bearer secret-token-1" http://localhost:5000/
HTTP頭信息”Authorization: Bearer secret-token-1″,”Bearer”就是指定的scheme,”secret-token-1″就是待驗证的Token。在上例中,”secret-token-1″對應著用戶名”John”,所以Token驗证成功,Curl命令會返回響應内容”Hello, John!”。
使用itsdangerous庫來管理令牌
itsdangerous庫提供了對信息加簽名(Signature)的功能,我們可以通過它來生成並驗证令牌。使用前,先記得安裝”pip install itsdangerous”。現在,讓我們先來産生令牌,並列印出來看看:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer app = Flask(__name__) app.config['SECRET_KEY'] = 'secret key here' serializer = Serializer(app.config['SECRET_KEY'], expires_in=1800) users = ['John', 'Susan'] for user in users: token = serializer.dumps({'username': user}) print('Token for {}: {}\n'.format(user, token))
這裡實例化了一個針對JSON的簽名序列化對象serializer,它是有時效性的,30分鍾後序列化後的簽名即會失效。讓我們運行下程序,在控制台上,會看到類似下面的内容:
Token for John: eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ2MzUzMzY4MCwiaWF0IjoxNDYzNTMxODgwfQ.eyJ1c2VybmFtZSI6IkpvaG4ifQ.ox-64Jbd2ngjQMV198nHYUsJ639KIZS6RJl48tC7-DU
Token for Susan: eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ2MzUzMzY4MCwiaWF0IjoxNDYzNTMxODgwfQ.eyJ1c2VybmFtZSI6IlN1c2FuIn0.lRx6Z4YZMmjCmga7gs84KB44UIadHYRnhOr7b4AAKwo
接下來,改寫”verify_token()”方法:
@auth.verify_token def verify_token(token): g.user = None try: data = serializer.loads(token) except: return False if 'username' in data: g.user = data['username'] return True return False
我們通過序列化對象的”load()”方法,將簽名反序列化爲JSON對象,也就是Python裡的字典。然後獲取字典中的用戶名,如果成功則返回True,否則返回False。這樣,就實現了加密後的令牌認证了,讓我們用Curl測試一下,還記得剛才控制台上列印出的令牌嗎?
curl -X GET -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ2MzUzMzY4MCwiaWF0IjoxNDYzNTMxODgwfQ.eyJ1c2VybmFtZSI6IkpvaG4ifQ.ox-64Jbd2ngjQMV198nHYUsJ639KIZS6RJl48tC7-DU" http://localhost:5000/
MultiAuth: 多重認证
Flask-HTTPAuth擴展還支持幾種不同認证的組合,比如上面我們介紹了HTTPBasicAuth和HTTPTokenAuth,我們可以將兩者組合在一起,其中任意一個認证通過,即可以訪問應用視圖。實現起來也很簡單,只需將不同的認证實例化爲不同的對象,並將其傳入MultiAuth對象即可。大體代碼如下:
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth, MultiAuth ... basic_auth = HTTPBasicAuth() token_auth = HTTPTokenAuth(scheme='Bearer') multi_auth = MultiAuth(basic_auth, token_auth) ... @basic_auth.verify_password ... @token_auth.verify_token ... @basic_auth.error_handler ... @token_auth.error_handler ... @app.route('/') @multi_auth.login_required def index(): return 'Hello, %s!' % g.user
這裡,每個認证都有自己的驗证和錯誤處理函數,不過在視圖上,我們使用”@multi_auth.login_required”來實現多重認证。大家可以使用Curl命令試驗下。
HTTPDigestAuth:
暫無舉例
詳情請點擊Flask 擴展 HTTP認证--flask-httpAuth
return 返回問題
例如下面的代碼返回的是空的,因爲瀏覽器會解析html標簽,但是我們標簽裡面沒有内容所以無任何顯示
@app.route('/hello') def hello(): # status code 200,404, 301 # contert-type http headers # content-type = text/html # Response return '<html></html>'
如果想當做普通字符串列印出來就需要返回的content-type=text/plain,如下圖所示,需要導入flask的make_response
@app.route('/hello') def hello(): # status code 200, 404, 301 # content-type http headers # content-type = text/html # Response headers = { 'content-type": 'text/plain' } response = make_response('<html></html>', 404) response.headers = headers return response
現在我們在做一個有意思的測試(轉發)
@app.route('/hello') def hello(): # status code 200, 404, 301 # content-type http headers # content-type = text/html # Response headers = { 'content-type': 'text/plain', 'location': 'http://www.bing.com' } response= make_response('<html></html> ', 301) response.headers = headers return response
測試發現直接轉發到了bing的網站
上面還有一種簡單的寫法就是如下圖所示:
@app.route("/hello") def hello(): # status code 200, 404, 301 # content-type http headers # content-type = text/html # Response headers = { 'content-type': 'application/json', 'location': 'http://www.bing.com' } # response = make_response (" <html> </html>", 301) # response . headers = headers return '<html></html>', 301, headers
上面我們用逗號分隔的形式其實是我們的flask的元組,當你返回爲一個元組的時候flask内部還是會把它自動變成一個response對象的。在返回回去。
改成下面這樣,就是返回json格式的數據了,其實這就是web返回的本質,返回的本質都是都是字符串,只不過控制的因素在這個context-type,他控制了我們的客戶耑在接收到我們的返回的時候要怎麽樣的去解釋我們的返回内容。
headers = { 'content-type': 'application/json', location': 'http://www.bing.com' }
flask-login 的使用
當用戶登錄成功之後我們要産生一個票據,並且把這個票據寫入cookie中,我們不僅負責寫入票據還要負責讀取票據,並且要管理這個票據,整個的登陸機制是非常繁瑣的,所以我們自己去用cookie實現這一整套的管理機制是非常不明智的,很幸運的是flask給我們提供了插件flask-login,可以完全用這個插件來管理登錄信息。
導入插件
from flask_ login import LoginManage