flask開發restful api系列(2)

来源:http://www.cnblogs.com/yueerwanwan0204/archive/2016/03/28/5329653.html
-Advertisement-
Play Games

繼續上一章所講,上一章我們最後面說道,雖然這個是很小的程式,但還有好幾個要優化的地方。先複製一下老的view.py代碼。 其中驗證token的方法,已經重疊了,python教我們,永遠不要重覆自己的代碼,這是很醜陋的行為。今天我們把它換成一個裝飾器,然後再把redis調整一下,看看代碼會不會簡潔很多 ...


  繼續上一章所講,上一章我們最後面說道,雖然這個是很小的程式,但還有好幾個要優化的地方。先複製一下老的view.py代碼。

 1 # coding:utf-8
 2 from flask import Flask, request, jsonify
 3 from model import User, db_session
 4 import hashlib
 5 import time
 6 import redis
 7 
 8 app = Flask(__name__)
 9 redis_store = redis.Redis(host='localhost', port=6380, db=4, password='dahai123')
10 
11 
12 @app.route('/')
13 def hello_world():
14     return 'Hello World!'
15 
16 
17 @app.route('/login', methods=['POST'])
18 def login():
19     phone_number = request.get_json().get('phone_number')
20     password = request.get_json().get('password')
21     user = User.query.filter_by(phone_number=phone_number).first()
22     if not user:
23         return jsonify({'code': 0, 'message': '沒有此用戶'})
24 
25     if user.password != password:
26         return jsonify({'code': 0, 'message': '密碼錯誤'})
27 
28     m = hashlib.md5()
29     m.update(phone_number)
30     m.update(password)
31     m.update(str(int(time.time())))
32     token = m.hexdigest()
33 
34     redis_store.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})
35     redis_store.set('token:%s' % token, user.phone_number)
36     redis_store.expire('token:%s' % token, 3600*24*30)
37 
38     return jsonify({'code': 1, 'message': '成功登錄', 'nickname': user.nickname, 'token': token})
39 
40 
41 @app.route('/user')
42 def user():
43     token = request.headers.get('token')
44     if not token:
45         return jsonify({'code': 0, 'message': '需要驗證'})
46     phone_number = redis_store.get('token:%s' % token)
47     if not phone_number or token != redis_store.hget('user:%s' % phone_number, 'token'):
48         return jsonify({'code': 2, 'message': '驗證信息錯誤'})
49 
50     nickname = redis_store.hget('user:%s' % phone_number, 'nickname')
51     return jsonify({'code': 1, 'nickname': nickname, 'phone_number': phone_number})
52 
53 
54 @app.route('/logout')
55 def logout():
56     token = request.headers.get('token')
57     if not token:
58         return jsonify({'code': 0, 'message': '需要驗證'})
59     phone_number = redis_store.get('token:%s' % token)
60     if not phone_number or token != redis_store.hget('user:%s' % phone_number, 'token'):
61         return jsonify({'code': 2, 'message': '驗證信息錯誤'})
62 
63     redis_store.delete('token:%s' % token)
64     redis_store.hmset('user:%s' % phone_number, {'app_online': 0})
65     return jsonify({'code': 1, 'message': '成功註銷'})
66 
67 
68 @app.teardown_request
69 def handle_teardown_request(exception):
70     db_session.remove()
71 
72 if __name__ == '__main__':
73     app.run(debug=True, host='0.0.0.0', port=5001)

其中驗證token的方法,已經重疊了,python教我們,永遠不要重覆自己的代碼,這是很醜陋的行為。今天我們把它換成一個裝飾器,然後再把redis調整一下,看看代碼會不會簡潔很多。

 1 # coding:utf-8
 2 from flask import Flask, request, jsonify
 3 from model import User, db_session
 4 import hashlib
 5 import time
 6 import redis
 7 from functools import wraps
 8 
 9 app = Flask(__name__)
10 redis_store = redis.Redis(host='localhost', port=6380, db=4, password='dahai123')
11 
12 
13 def login_check(f):
14     @wraps(f)
15     def decorator(*args, **kwargs):
16         token = request.headers.get('token')
17         if not token:
18             return jsonify({'code': 0, 'message': '需要驗證'})
19         
20         phone_number = redis_store.get('token:%s' % token)
21         if not phone_number or token != redis_store.hget('user:%s' % phone_number, 'token'):
22             return jsonify({'code': 2, 'message': '驗證信息錯誤'})
23 
24         return f(*args, **kwargs)
25     return decorator
26 
27 
28 @app.route('/login', methods=['POST'])
29 def login():
30     phone_number = request.get_json().get('phone_number')
31     password = request.get_json().get('password')
32     user = User.query.filter_by(phone_number=phone_number).first()
33     if not user:
34         return jsonify({'code': 0, 'message': '沒有此用戶'})
35 
36     if user.password != password:
37         return jsonify({'code': 0, 'message': '密碼錯誤'})
38 
39     m = hashlib.md5()
40     m.update(phone_number)
41     m.update(password)
42     m.update(str(int(time.time())))
43     token = m.hexdigest()
44     
45     pipeline = redis_store.pipeline()
46     pipeline.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})
47     pipeline.set('token:%s' % token, user.phone_number)
48     pipeline.expire('token:%s' % token, 3600*24*30)
49     pipeline.execute()
50 
51     return jsonify({'code': 1, 'message': '成功登錄', 'nickname': user.nickname, 'token': token})
52 
53 
54 @app.route('/user')
55 @login_check
56 def user():
57     token = request.headers.get('token')
58     phone_number = redis_store.get('token:%s' % token)
59 
60     nickname = redis_store.hget('user:%s' % phone_number, 'nickname')
61     return jsonify({'code': 1, 'nickname': nickname, 'phone_number': phone_number})
62 
63 
64 @app.route('/logout')
65 @login_check
66 def logout():
67     token = request.headers.get('token')
68     phone_number = redis_store.get('token:%s' % token)
69     
70     pipeline = redis_store.pipeline()
71     pipeline.delete('token:%s' % token)
72     pipeline.hmset('user:%s' % phone_number, {'app_online': 0})
73     pipeline.execute()
74     return jsonify({'code': 1, 'message': '成功註銷'})
75 
76 
77 @app.teardown_request
78 def handle_teardown_request(exception):
79     db_session.remove()
80 
81 if __name__ == '__main__':
82     app.run(debug=True, host='0.0.0.0', port=5001)

加了一個裝飾器,是不是簡潔了很多?每次需要驗證的時候,只需要一個login_check就可以了,這樣就變得非常簡潔,而且脈絡清晰。redis也改成了管道執行,pipeline,防止執行到一半,被掐斷。

可是,可是,我還是覺得不簡潔,看user, logout的代碼中重覆的地方。

token = request.headers.get('token')
phone_number = redis_store.get('token:%s' % token)

每次都有這兩句,要是將來還有其他值怎麼辦?上面不剛說,永遠不要重覆自己的代碼嗎?

好,我們再寫一個函數,看下麵代碼

 1 # coding:utf-8
 2 from flask import Flask, request, jsonify, g
 3 from model import User, db_session
 4 import hashlib
 5 import time
 6 import redis
 7 from functools import wraps
 8 
 9 app = Flask(__name__)
10 redis_store = redis.Redis(host='localhost', port=6380, db=4, password='dahai123')
11 
12 
13 def login_check(f):
14     @wraps(f)
15     def decorator(*args, **kwargs):
16         token = request.headers.get('token')
17         if not token:
18             return jsonify({'code': 0, 'message': '需要驗證'})
19         
20         phone_number = redis_store.get('token:%s' % token)
21         if not phone_number or token != redis_store.hget('user:%s' % phone_number, 'token'):
22             return jsonify({'code': 2, 'message': '驗證信息錯誤'})
23 
24         return f(*args, **kwargs)
25     return decorator
26 
27 
28 @app.before_request
29 def before_request():
30     token = request.headers.get('token')
31     phone_number = redis_store.get('token:%s' % token)
32     if phone_number:
33         g.current_user = User.query.filter_by(phone_number=phone_number).first()
34         g.token = token
35     return
36 
37 
38 @app.route('/login', methods=['POST'])
39 def login():
40     phone_number = request.get_json().get('phone_number')
41     password = request.get_json().get('password')
42     user = User.query.filter_by(phone_number=phone_number).first()
43     if not user:
44         return jsonify({'code': 0, 'message': '沒有此用戶'})
45 
46     if user.password != password:
47         return jsonify({'code': 0, 'message': '密碼錯誤'})
48 
49     m = hashlib.md5()
50     m.update(phone_number)
51     m.update(password)
52     m.update(str(int(time.time())))
53     token = m.hexdigest()
54 
55     pipeline = redis_store.pipeline()
56     pipeline.hmset('user:%s' % user.phone_number, {'token': token, 'nickname': user.nickname, 'app_online': 1})
57     pipeline.set('token:%s' % token, user.phone_number)
58     pipeline.expire('token:%s' % token, 3600*24*30)
59     pipeline.execute()
60 
61     return jsonify({'code': 1, 'message': '成功登錄', 'nickname': user.nickname, 'token': token})
62 
63 
64 @app.route('/user')
65 @login_check
66 def user():
67     user = g.current_user
68 
69     nickname = redis_store.hget('user:%s' % user.phone_number, 'nickname')
70     return jsonify({'code': 1, 'nickname': nickname, 'phone_number': user.phone_number})
71 
72 
73 @app.route('/logout')
74 @login_check
75 def logout():
76     user = g.current_user
77 
78     pipeline = redis_store.pipeline()
79     pipeline.delete('token:%s' % g.token)
80     pipeline.hmset('user:%s' % user.phone_number, {'app_online': 0})
81     pipeline.execute()
82     return jsonify({'code': 1, 'message': '成功註銷'})
83 
84 
85 @app.teardown_request
86 def handle_teardown_request(exception):
87     db_session.remove()
88 
89 if __name__ == '__main__':
90     app.run(debug=True, host='0.0.0.0', port=5001)

我們在代碼中加了一個before_request,這個函數就是在每個request發起的時候,如果已經驗證了,我們把當前g.current_user和g.token設置一下,這樣每次需要獲取當前用戶的時候,直接找g.current_user就可以了,是不是簡單了太多太多?好了,今天到此為止,下一章,我們講怎麼利用七牛上傳圖片。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們經常會遇到在Winform或是WPF中點擊鏈接或按鈕打開某個指定的網址, 或者是需要打開電腦中某個指定的硬碟分區及文件夾, 甚至是"控制面板"相關的東西, 那麼如何做呢? 答案是使用System.Diagnostics.Process.Start()。它的作用是調用外部的命令。 先來看看它的調用 ...
  • MVC中Bundles可以提高代碼的可重用性 我每個頁面都需要用到這十幾個JS+CSS 當我把MVC發佈到伺服器以後,Bundles中的JS和CSS會失效的時候 寶寶的心裡是崩潰的.... 查了很多資料 都說刪除一個BIN中的DLL就好了 但是每次重新生成的時候 那個DLL還會出現 並且刪除後發佈到 ...
  • 移植WPF軟體到uwp時碰到用作對話框的Window有多種處理選擇。我個人認為最省事的是用ContentDialog模擬Window。 比如你想把上面這個WPF窗體弄到uwp裡面去 1.修改ContentDialog的預設樣式 新建一個模板化控制項RoundCornerContentDialog 讓它 ...
  • 在MVC系統中,我們接受數據,運用的是ModelBinder 的技術。 MVC學習系列——ActionResult擴展在這個系列中,我們自定義了XmlResult的返回結果。 那麼是不是意味著能POST一個XML數據類型給我們項目,這樣的話,我們需要自定義一個XmlModelBinder來接受XML ...
  • 前言 孔子說:"軟體是對客觀世界的抽象"。 首先聲明,這裡的"三維導航"和地圖沒一毛錢關係,"四核驅動"和硬體也沒關係,而是為了複雜的應用而發明創造的導航邏輯。說這是發明創造,也不是危言聳聽,因為它完全突破了傳統意義的頁面導航概念,看完了本博客以後,相信會讓你腦洞大開。當然這也是一種嘗試,只有UWP ...
  • 在MVC中,Filter也是可以擴展的。在此,本人對Filter的理解就是AOP,不知道各位大俠,有什麼高的見解,呵呵。。。 首先MVC四大過濾神器IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter。 在此之前,我們先安 ...
  • Session.Abandon();//清除全部Session//清除某個SessionSession["UserName"] = null;Session.Remove("UserName"); Session["aa"]=變數名; //賦值 ...
  • Julia中文教程資源.txt 2016年3月28日 05:18:32 codegay 本文更新在這裡: https://github.com/FGFW/julia science and technology 官方的資料總是少不了需要自己去看的. julia 英文手冊 http://docs.ju ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...