把注册业务逻辑拆分三个接口:
注册的本质是在上新建一个资源信息,其对应的方法是post,
而图片验证码的生成与获取,短信验证码的生成与获取是get请求。
从接口的风格来考虑拆分了三个。在一个也可以实现。
为什么前后端分离?
重点:提高(后端接口)代码的复用性比如任意web项目都有用户模块,如使用Django的一些试图类或视图函数内容不能用了,因为其严重依赖于模板。如前后端分离调用接口代码基本不变,大量的return直接返回给浏览器了解耦,快速开发,责任分清,Django有MVT开发就慢了吗,前后端不分离使用?重点就是提高(后端接口)代码的复用性其他是附属功能
生成图片验证码
1 调用captcha扩展包,生成图片验证码,name,text,image2 本地存储图片验证码,使用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/',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/',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())