最近兩三個月的時間,斷斷續續邊學邊做完成了一個微信公眾號頁面的開發工作。這是一個快遞系統,主要功能有用戶管理、寄收件地址管理、用戶下單,訂單管理,訂單查詢及一些宣傳頁面等。本文主要細數下開發過程中遇到的各種坑,也算是另外一種總結吧。
1. 開發語言及框架
Python + Flask + Bootstrap,數據庫使用的是MySQL
2. 相關文檔及Lib庫
1) Bootstrap官方文檔
http://v3.bootcss.com/getting-started/
2) 微信公眾號開發文檔
https://mp.weixin.qq.com/wiki
3) Python 微信SDK
https://github.com/zwczou/weixin-python/
4) PDF 《FlaskWeb開發:基於Python的Web應用開發實戰》
3. 那些坑
3.1 微信
3.1.1 微信登陸
首先你需要仔細閱讀官方文檔,簡單來說微信登陸有如下幾步:
1) 生成微信認證跳轉URL,注意有`snsapi_base`跟`snsapi_userinfo`兩種方式,前者是靜默授權只獲取用戶openid,后者需要用戶手動同意獲取用戶基本信息
2) 獲取access_token
3) 獲取用戶信息
解決微信OAuth2.0網頁授權只能設置一個回調域名的問題,參考
https://github.com/HADB/GetWeixinCode
Python微信SDK,參考
https://github.com/zwczou/weixin-python/
3.1.2 模版消息
登陸微信公眾平台 -> 功能 -> 模版消息,選擇右側模版消息接口文檔 ,即可查看詳細的接口文檔。
主要步驟如下:
1)獲取access_token,其中token有效期為7200s,而且微信限制了每天的調用次數,這里使用functools.lru_cache維護了一個token的內存緩存
2)獲取模版ID
3)請求接口
POST URL: https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
POST Data: 請求包為json
3.2 MySQL
3.2.1 編碼問題
MySQL遇到最大的坑還是編碼問題,因為涉及到獲取微信用戶名含有各種emoji表情的問題,需要設置字符編碼為utf8mb4,具體可以參考這篇文章(
https://mathiasbynens.be/notes/mysql-utf8mb4),然而設置成功在Flask SQLAlchemy配置app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root@localhost:3306/test?charset=utf8mb4'后,運行報錯sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (2019, "Can't initialize character set utf8mb4 (path: C:\\mysql\\\\share\\charsets\\)"),解決無果。
最終解決方案是:
1) Flask SQLAlchemy配置app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root@localhost:3306/test?charset=utf8'
2) 微信用戶名寫入數據庫時使用repr()方法將寫入原始unicode字符,讀取的時候再使用eval()進行轉換
3.2.2 SQLAlchemy查詢數據轉換為Dict
for u in session.query(User).all(): u = dict(u.__dict__) u.pop('_sa_instance_state', None)
參考文章:
3.4 Flask
3.4.1 cookie相關
1) 設置cookie
@app.route('/set_cookie') def set_cookie(): response=make_response('Hello World'); response.set_cookie('Name','Joo') return response
2) 獲取cookie
@app.route('/get_cookie') def get_cookie(): name=request.cookies.get('Name') return name
3) 刪除cookie
設置過期時間為0
@app.route('/del_cookie') def del_cookie(): response=make_response('delete cookie') response.set_cookie('Name','',expires=0) return response
使用delete_cookie方法
@app.route('/del_cookie') def del_cookie(): response=make_response('delete cookie') response.delete_cookie('Name') return response
3.4.2 flask.make_response() 實例
3.4.3 詳細解讀Jquery各Ajax函數:$.get(),$.post(),$.ajax(),$.getJSON()
3.4.4 bootstrap對話框插件
3.4.5 Flask flash增加link
3.4.6 HTML顏色編碼
3.4.7 Python緩存
微信獲取access_token時有效期是7200s,而且微信限制了每天的調用頻率(2000次/天),所以簡單使用lru_cache在內存中維護了一個token緩沖,示例代碼如下:
@lru_cache(None) def getAccessToken(): url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}'.format(app_id, app_secret) r = requests.get(url) access_token = r.json().get('access_token') time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print '[{0}] getAccessToken Result:\t{1}'.format(time_now, r.text) return access_token, datetime.now() def getToken(): token, t = getAccessToken() if (datetime.now() - t).seconds > 3600: getAccessToken.cache_clear() token, t = getAccessToken() return token else: return token
參考:
3.4.8 修改Bootstrap使用國內源
由於默認Bootstrap使用的CDN是http://cdnjs.cloudflare.com,國內訪問較慢,所以需要修改默認CDN為國內源。
找到C:\Python27\Lib\site-packages\flask_bootstrap\__init__.py(C:\Python27 為你當前Python版本路徑),在文件最后找到如下代碼:
bootstrap = lwrap( WebCDN('//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/%s/' % BOOTSTRAP_VERSION), local) jquery = lwrap( WebCDN('//cdnjs.cloudflare.com/ajax/libs/jquery/%s/' % JQUERY_VERSION), local) html5shiv = lwrap( WebCDN('//cdnjs.cloudflare.com/ajax/libs/html5shiv/%s/' % HTML5SHIV_VERSION)) respondjs = lwrap( WebCDN('//cdnjs.cloudflare.com/ajax/libs/respond.js/%s/' % RESPONDJS_VERSION))
替換為國內源cdn.bootcss.com,代碼如下:
bootstrap = lwrap( WebCDN('//cdn.bootcss.com/twitter-bootstrap/%s/' % BOOTSTRAP_VERSION), local) jquery = lwrap( WebCDN('//cdn.bootcss.com/jquery/%s/' % JQUERY_VERSION), local) html5shiv = lwrap( WebCDN('//cdn.bootcss.com/html5shiv/%s/' % HTML5SHIV_VERSION)) respondjs = lwrap( WebCDN('//cdn.bootcss.com/respond.js/%s/' % RESPONDJS_VERSION))
參考資料:
3.4.9 flask部署
flask通常在Linux上部署方式是 flask + wsgi + nginx,windows上則是flask + iis + nginx。這里實際部署的環境是Windows Server 2007,由於項目實際訪問量較小的關系,最終選用簡單的flask + tornado部署方式。
在flask項目里原來的入口程序假設為run.py的同級目錄添加tornado_server.py,內容如下:
# coding:utf-8 from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from run import app http_server = HTTPServer(WSGIContainer(app)) # address為實際訪問URL,port為端口號 http_server.listen(port=5000, address="127.0.0.1") IOLoop.instance().start()
使用python tornado_server.py即可啟動。
參考文檔: