把注冊業務邏輯拆分三個接口:
注冊的本質是在服務器上新建一個資源信息,其對應的方法是post,
而圖片驗證碼的生成與獲取,短信驗證碼的生成與獲取是get請求。
從接口的風格來考慮拆分了三個。在一個也可以實現。
為什么前后端分離?
重點:提高(后端接口)代碼的復用性比如任意web項目都有用戶模塊,如使用Django的一些試圖類或視圖函數內容不能用了,因為其嚴重依賴於模板。如前后端分離調用接口代碼基本不變,大量的return直接返回給瀏覽器了
解耦,快速開發,責任分清,Django有MVT開發就慢了嗎,前后端不分離使用?重點就是提高(后端接口)代碼的復用性其他是附屬功能
生成圖片驗證碼
1 調用captcha擴展包,生成圖片驗證碼,name,text,image 2 本地存儲圖片驗證碼,使用redis數據庫(如發生異常,保存圖片驗證碼失敗,記錄日志信息) 3 返回圖片本身,設置響應的content-type(如未發生異常,則返回圖片)
實現代碼:
# coding=utf-8 # 導入藍圖對象 from . import api # 導入圖片驗證碼擴展 from ihome.utils.captcha.captcha import captcha # 導入數據庫實例 from ihome import redis_store,constants,db # 導入flask內置的對象 from flask import current_app,jsonify,make_response,request,session # 導入自定義的狀態碼 from ihome.utils.response_code import RET # 導入模型類 from ihome.models import User # 導入雲通訊擴展包 from ihome.utils import sms # 導入正則模塊 import re # 導入random模塊,構造短信隨機數 import random @api.route('/imagecode/<image_code_id>',methods=['GET']) def generate_image_code(image_code_id): # 調用captcha擴展包 name,text,image = captcha.generate_captcha() # 調用redis數據庫實例,存儲圖片驗證碼 try: redis_store.setex('ImageCode_' + image_code_id,constants.IMAGE_CODE_REDIS_EXPIRES,text) except Exception as e: # 調用應用上下文,記錄項目錯誤日志信息 current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg='保存圖片驗證碼失敗') # 如果未發生異常,返回圖片本身 else: # 使用響應對象,用來返回圖片 response = make_response(image) # 設置響應報文的Content-Type = 'image/jpg' response.headers['Content-Type'] = 'image/jpg' # 返回響應response return response
發送短信
獲取參數–校驗參數–查詢數據–返回結果
1. 獲取參數,圖片驗證碼和編號 2. 校驗參數的完整性,mobile(手機號),text(圖片驗證碼),id(圖片驗證碼id)(參數全部存在,如少,則返回參數不完整,提醒輸入) 3. 校驗mobile手機號格式,使用正則(如有錯誤,則返回手機格式不正確) 4. 獲取本地存儲的真實圖片驗證碼 5. 判斷獲取,圖片驗證碼是否存在過期(從redis數據庫不一定獲取得到,try一下) 6. 刪除圖片驗證碼(因為圖片驗證碼只能使用一次) 7. 比較圖片驗證碼(判斷客戶輸入與圖片生成是否一致,可以忽略大小寫) 8. 生成短信內容,隨機數(短信驗證碼) 9. 查詢數據庫,判斷手機號是否已經注冊(是否查詢到,try一下) 10. 保存短信內容到redis中(生成短信驗證碼) 11. 調用雲通訊發送短信 12. 保存返回結果,判斷是否發送成功 13. 返回結果 ---------------------
實現代碼:
@api.route('/smscode/<mobile>',methods=['GET']) def send_sms_code(mobile): # 獲取參數 image_code = request.args.get('text') image_code_id = request.args.get('id') # 檢查參數的完整性,any--all if not all([mobile,image_code,image_code_id]): return jsonify(errno=RET.PARAMERR,errmsg='參數不完整') # 校驗手機號格式是否滿足 if not re.match(r'1[3456789]\d{9}',mobile): return jsonify(errno=RET.PARAMERR,errmsg='手機號格式錯誤') # 檢查圖片驗證碼,獲取本地存儲的真實圖片驗證碼 try: real_image_code = redis_store.get('ImageCode_' + image_code_id) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg='查詢圖片驗證碼失敗') # 校驗獲取結果 if not real_image_code: return jsonify(errno=RET.NODATA,errmsg='圖片驗證碼過期') # 刪除圖片驗證碼,圖片驗證碼只能操作一次 try: redis_store.delete('ImageCode_' + image_code_id) except Exception as e: current_app.logger.error(e) # 比較圖片驗證碼是否一致,忽略大小寫 if real_image_code.lower() != image_code.lower(): return jsonify(errno=RET.DATAERR,errmsg='圖片驗證碼錯誤') # 查詢數據庫,判斷手機號是否已經注冊 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg='查詢數據庫異常') else: # 判斷查詢結果 if user: return jsonify(errno=RET.DATAEXIST,errmsg='手機號已注冊') # 構造短信隨機碼 sms_code = '%06d' % random.randint(0,999999) # 保存短信隨機碼 try: redis_store.setex('SMSCode_' + mobile,constants.SMS_CODE_REDIS_EXPIRES,sms_code) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg='保存短信驗證碼失敗') # 調用雲通訊擴展,發送短信 try: ccp = sms.CCP() # 調用雲通訊的模板方法發送短信 result = ccp.send_template_sms(mobile,[sms_code,constants.SMS_CODE_REDIS_EXPIRES/60],1) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.THIRDERR,errmsg='發送短信異常') # 判斷result是否發送成功 # result = 0 if 0 == result: return jsonify(errno=RET.OK,errmsg='發送成功') else: return jsonify(errno=RET.THIRDERR,errmsg='發送失敗')
注冊用戶
1. 獲取參數,get_json()(獲取請求體參數手機號短信號密碼) 2. 校驗參數存在 3. 獲取詳細的參數,mobile,sms_code,password 4. 校驗手機號格式 5. 校驗短信驗證碼 6. 獲取本地存儲的真實短信驗證碼 7. 判斷查詢結果 8. 比較短信驗證碼是否正確 9. 刪除短信驗證碼(如果一致登入,並刪除比較后的驗證碼) 10. 構建模型對象,准備保存用戶數據 user = User(mobile=mobile,name=mobile) user.password = password 11.使用數據庫會話對象提交到數據庫 12.緩存用戶信息,到redis。session['user_id']=user_id 13.返回結果,附屬信息data=user.to_dict() ---------------------
實現代碼:
@api.route('/users',methods=['POST']) def register(): # 獲取參數 user_data = request.get_json() # 判斷獲取結果 if not user_data: return jsonify(errno=RET.PARAMERR,errmsg='參數錯誤') # 獲取詳細的參數信息,mobile,sms_code,password # user_data['mobile'] mobile = user_data.get('mobile') sms_code = user_data.get('sms_code') password = user_data.get('password') # 檢查參數的完整性 if not all([mobile,sms_code,password]): return jsonify(errno=RET.PARAMERR,errmsg='參數缺失') # 手機號格式檢查 if not re.match(r'1[3456789]\d{9}',mobile): return jsonify(errno=RET.PARAMERR,errmsg='手機號格式錯誤') # 判斷用戶是否已經注冊 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR, errmsg='查詢數據庫異常') else: # 判斷查詢結果 if user: return jsonify(errno=RET.DATAEXIST, errmsg='手機號已注冊') # 獲取本地存儲的真實短信驗證碼 try: real_sms_code = redis_store.get('SMSCode_' + mobile) except Exception as e: current_app.logger.error(e) return jsonify(errno=RET.DBERR,errmsg='查詢數據庫異常') # 判斷查詢redis的結果 if not real_sms_code: return jsonify(errno=RET.NODATA,errmsg='短信驗證碼過期') # 直接比較短信驗證碼是否正確 if real_sms_code != str(sms_code): return jsonify(errno=RET.DATAERR,errmsg='短信驗證碼錯誤') # 刪除短信驗證碼 try: redis_store.delete('SMSCode_' + mobile) except Exception as e: current_app.logger.error(e) # 准備保存用戶注冊信息 user = User(mobile=mobile,name=mobile) # 調用模型類中的方法generate_password_hash,對密碼進行加密sha256處理 user.password = password # 提交數據到數據庫 try: db.session.add(user) db.session.commit() except Exception as e: current_app.logger.error(e) # 如果提交數據發生異常,需要進行回滾 db.session.rollback() return jsonify(errno=RET.DBERR,errmsg='保存用戶信息失敗') # 緩存用戶信息 session['user_id'] = user.id session['mobile'] = mobile session['name'] = mobile # 返回結果 return jsonify(errno=RET.OK,errmsg='OK',data=user.to_dict())