在項目中我們會遇到一些應用場景,在訪問某個URL後重定向到上一個頁面。比如我們在訪問一個博客頁面時,點擊評論鏈接就直接重定向到登錄頁面,當用戶登錄後合理的行爲是跳轉到評論頁面而不是主頁面。如下示例:
def func_a():
return "<h2>這是a頁面</h2><a href='{}'>do_something</a>".format(url_for('do_something'))
@app.route('/b')
def func_b():
return "<h2>這是b頁面</h2><a href='{}'>do_something</a>".format(url_for('do_something'))
@app.route('/do-something')
def do_something():
print('do things.....')
return redirect(url_for('test'))
這裏我們訪問完這個視圖後直接調到固定的test頁面中,但是我們希望重定向到原來的頁面:
1.獲取上個頁面的url
(1)HTTP.referrer
是一個用來記錄請求發源地址的,即訪問來源。
所以do_something可以返回:
return redirect(request.referrer)
但是referrer字段在很多情況下是空值,所以爲了保險返回固定頁面:
return redirect(request.referrer or url_for('test'))
(2)查詢參數
在URL中手動加入包含當前頁面url的查詢參數,這個參數一般命名爲next。
@app.route('/a')
def func_a():
return "<h2>這是a頁面</h2><a href='{}'>do_something</a>".format(url_for('do_something', next=request.full_path))
@app.route('/b')
def func_b():
return "<h2>這是b頁面</h2><a href='{}'>do_something</a>".format(url_for('do_something', next=request.full_path))
這裏的request.full_path獲取的是當前頁面的完整路徑,在do_something視圖中獲取next值,重定向到指定路徑:
return redirect(request.args.get('next'))
爲避免next爲空,可以如下:
return redirect(request.args.get('next'),url_for('test'))
爲了更全面的考慮,可以將兩者結合,將兩者封裝到函數中,如下:
def redirect_back(default='test', **kwargs):
for target in request.args.get('next'), request.referrer:
if target:
return redirect(target)
return redirect(url_for(default, **kwargs))
此方法同時兼顧了多種情況。
相應的do_something視圖中的返回改爲:
@app.route('/do-something')
def do_something():
print('do things.....')
return redirect_back()
2.對URL進行安全驗證
在重定向的過程中url的安全問題值得考慮,如next的值爲一個跳向釣魚網的鏈接,那後果不堪設想。
爲了確保URL安全,就要對next的值進行驗證:
def is_safe_url(url):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, url))
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
此方法將next的url作爲參數,並通過request.host_url獲取程序內的主機URL,然後使用urljoin()函數將url轉成絕對url。接着用urlparse()函數解析兩個url,最後對目標url的url模式和主機地址進行驗證,確保只有程序內的url才能被返回。
通過此方法驗證redirect_back()中的next值和referrer:
def redirect_back(default='test', **kwargs):
for target in request.args.get('next'), request.referrer:
if not target:
continue
print(is_safe_url(target))
if is_safe_url(target):
return redirect(target)
return redirect(url_for(default, **kwargs))
完成跳轉的上個頁面。
參考資料:《Flask Web 開發實戰》