1.相关配置信息
app.config.from_object(Config)
指的是从Config这个配置类里面加载配置信息,只有使用数据库的时候,才会加载里面的配置信息.
SQLALCHEMY_DATABASE_URI="mysql://root:mysql@127.0.0.1:3306/information"
指的是数据库的连接地址,里面具体的参数大家应该都很熟悉了,就不一一解读了,需要注意的是,在连接数据库之前,一定要在mysql
里面先建好我们需要的数据库,否则连接的时候会出现错误.
SQLALCHEMY_TRACK_MODIFICATIONS = False
这一个虽然经常使用,但是我却一直忘记了它的具体含义,今天特意的查了一下资料,这项配置信息指的是动态追踪修改设置,默认是True
,将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它,禁用之后只会提示警告。所以我们一般都是改为False
,
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
设置是否在每次连接结束后自动提交数据库中的变动,如果不设置的话,每次对数据库进行了增删改之后,就只能手动提交了,比较麻烦.
db = SQLAlchemy(app)
是创建一个SQLAlchemy
对象db,而且将app中所有的配置信息读取出来,加载到对象db中(SQLAlchemy
就是一个关系型数据库框架,是对数据库的抽象,我们只需要输入简单的命令,它就会帮我们将其翻译成SQL语句,与数据库进行相关的操作,是通过我们创建出来的对象操作数据库的,虽然舍弃了一些性能的开销,但是大大简化了我们的操作.)
sr = StrictRedis(host='localhost', port=6379, db=0)
通过init创建对象,指定参数host、port与指定的服务器和端⼝连接,host默认为localhost,port默认为6379,db默认为0(这里的db可不是我们上面实例出来的对象,它指的是redis中编号为0的数据库,redis中的数据库是没有名称的,它默认有16个编号0-15,哈哈,是不是这些知识都忘记了,我查了好久才查到),我们还可以在最后设置decode_responses
设置为True,就是将redis取出来的数据库自动解码.建立好后,我们就可以用创建出来的sr对象来和数据库进行交互了.
SESSION_TYPE = "redis"
设置session存储类型
SESSION_REDIS = StrictRedis(host=REDIS_HOST,port=REDIS_PORT)
指定session存储的redis服务器
SESSION_USE_SIGNER = True
设置签名存储,让 cookie 中的 session_id 被加密签名处理
PERMANENT_SESSION_LIFETIME = timedelta(days=2)
设置session有效期,两天时间
2.数据库迁移
关于数据库迁移:关于数据库迁移框架是为了在变更表结构的时候我们可以进行备份,更新的过程数据一般不会丢失,做降级的时候需要谨慎操作.
manager = Manager(app)
通过Manager类创建对象manager,用来管理app
migrate = Migrate(app,db)
使用Migrate来关联app和db,第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
manager.add_command('db',MigrateCommand)
manager是Flask-Script的实例,这条语句是在flask-Script中添加一个db命令,我们在操作的时候就可以使用db命令了
3.蓝图
蓝图简单的说就是一个存储操作方法的容器,我们如果是想应用实现模块化,高内聚低耦合,那么我们就需要使用flask中自带的蓝图了.蓝图在使用的时候分为了三个步骤:创建蓝图对象,在蓝图对象上进行操作(即装饰视图函数),然后就是在应用对象上注册蓝图,应用对象指的就是我们前面利用flask实例化出来的app对象.
user_blue=Blueprint("user",__name__,static_folder="static",url_prefix="/user",template_folder="templates")
创建蓝图对象,其中的参数下面进行详细的说明:
参数1: user,表示蓝图的名字,用来标识蓝图装饰的视图函数所属的蓝图
参数2: __name__,固定写法,表示的是蓝图所在包的名字
参数3: static_folder,用来存储静态文件的
参数4: url_prefix,给所有使用user_blue装饰的视图函数都加上一个访问前缀
参数5: template_folder,表示蓝图自己的模板文件, 如果app中也设置也自己的模板,如果文件名相同,那么优先访问app的
@user_blue.route('/user_login')
简单的来说就是将原来的应用对象app换成我们的蓝图对象
app.register_blueprint(user_blue)
将蓝图注册到应用对象app中
4.零散知识点
send_static_file
是系统访问静态文件所调用的方法
render_template('模板文件名',key=value)
是将数据携带到文件中进行展示,它会到templates文件夹中去寻找你需要的html页面
单例:单例模式是一个基础概念,在创建类的时候,第一次进入的其实不是__init__
方法,而是__new__
方法,进入new魔法方法是为了给下面创建一个对象.利用一个类每次返回相同的数值,每次都需要开辟内存,我们为了节约内存,需要选用单例方法,只开辟一次内存即可,以后每次需要,只需要返回第一次的值.
我们需要设置判断条件,进行判断是否是第一次进入。判断条件可以设置一个类属性,不能为实例属性是因为new方法是建立一个实例对象,实例对象都没有建,无法访问实例方法。我们只需要设置一个instence看它是否有值,先设置成None,节约内存。
自定义过滤器:有两种方法,我们重点掌握下面的这一种就可以了
1/先定义一个函数,这个函数就是要实现过滤功能的函数
2.然后使用app.add_template_filter('函数名','过滤器名称')
将我们自定义的过滤器添加到过滤器列表中
在一个 for 循环块中你可以访问这些特殊的变量:
变量 | 描述 |
loop.index | 当前循环迭代的次数(从 1 开始) |
loop.index0 | 当前循环迭代的次数(从 0 开始) |
下面两个功能是在首页中显示的,因此我们需要将下面两个功能写入到index.html中:
查询热门新闻排行,我们只需要将新闻根据点击量,按降序排列,然后查询前十条新闻,返回给前台进行渲染即可.
分类数据显示,我们需要到数据库中查询所有的分类信息,然后返回给前台,前端通过遍历分类信息,将分类数据显示到浏览器即可
首页的新闻展示,我们不能写在首页中了,因为我们在刷新或者滚动查看更多信息的时候,要获取更多的信息,如果是写在首页中,每次刷新都是显示一下首页中的数据,那么只能显示固定的几条新闻,因为浏览器有高度.我们需要的是新闻列表的部分,进行局部刷新,局部刷新的话,就需要使用ajax
下面是在新闻分类的时候查询的三种方式:
1 #判断新闻的分类是否为1 2 if cid == "1": 3 paginate = News.query.filter().order_by(News.create_time.desc()).paginate(page,per_page,False) 4 else: 5 paginate = News.query.filter(News.category_id == cid).order_by(News.create_time.desc()).paginate(page,per_page,False)
上面的挺好,但是不够灵活,因为在查询语句中条件写死了
1 # 改装,判断新闻的分类是否为1 2 filters = "" 3 if cid != "1": 4 filters = (News.category_id == cid) 5 paginate = News.query.filter(filters).order_by(News.create_time.desc()).paginate(page, per_page, False)
上面的这种方式比第一种灵活一点,filters = (News.category_id == cid)
,如果等于1,那么就是空的字符串,直接将所有的新闻按照时间进行降序排列,然后根据分页的条件,进行获取新闻
其中filters取出来的数据就是括号里面的语句,并不是一个括号和数据,下面我们做一个小实验:(上面是代码,下面是控制台输出的结果)
1filter = (123) 2print(filter) 3print(type(filter)) 4print(type((1))) 5----------------------------------------------- 6123 7<class 'int'> 8<class 'int'> 9
由此可见,括号里面有什么变量中存的就是什么,类型也一致.
1 filters = [] 2 if cid != "1": 3 filters.append(News.category_id == cid) 4 paginate = News.query.filter(*filters).order_by(News.create_time.desc()).paginate(page,per_page,False)
上面的这种方式,filter()中的*filters代表的是解包filter,如果没有数据,就是一个空的列表,有数据,就直接将查询语句拆出来放进过滤器中.这样做的好处是我们如果还有其他的条件,可以一起加进去,更加的灵活.
5.图片验证码验证的流程
1/前端中有专门的函数生成了验证码的一个编号,然后将这个编号提交给了后台去请求验证码的图片.
2/后台获取到此次验证码的编号和上一次验证码的编号(前端中生成验证码编号的函数每次生成编号的同时还会记录下上一次的编号,存下来,一起交给后台),
3/后台调用了captcha.generate_captcha()
来获取图片的验证码编号(这个编号我们不使用,我们使用的是前台发送过来的验证码编号),验证码的值和图片(二进制);
4/后台将图片保存在redis中,其中从前台获取的验证码编号作为key,验证码的值(就是图片验证码上的数字,字母)作为value,当然我们还要给这个键值对设置一个有效期
5/我们用从前台获取到的上一次验证码编号作为判断依据,如果有,那我们就将上一次的图片验证码在redis中保存的信息给删除,因为我们的redis服务器资源空间有限,防止有一种人(心里不爽或者恶作剧等重复点击验证码,使后台接收到过多的数据)恶性破坏,不能将每一次的验证码信息都保存下来,那样会撑爆服务器,我们就删除上一次,保存这一次的就好了.
6/当然,凡是涉及到与服务器连接的情况,我们都要考虑连接失败的情况,不能连接失败,前台直接崩掉或者无响应吧,给用户的体验极其不好,因此,我们就try一下,如果错误就记录在日志中,并且向浏览器发送操作失败的相关信息.
7/最后就是重中之重了,我们将图片返回给前台response=make_response(image_data)
,这样我们还可以自定义返回的响应报文的信息,比如自定义状态码,制定会议文本类型,告诉浏览器这是图片response.headers["Content-Type"] = "image/png"
,让浏览器用渲染图片的方式去渲染这个数据,要不然浏览器默认的是文本格式,会给用户展示一坨乱码.将这些设置好后,直接return response
将响应报文返回就好了
8/前端申请发送短信验证码的时候也用到了图片验证码,因为只有当验证码输入正确的时候,后台才能委托第三方给用户发送短信
9/前端申请发送短信验证码的时候,带上第一步生成的验证码编号和用户输入的验证码内容
10/后台接收到了前端的申请之后,拿着编号去redis中取出验证码图片上的数字字母,然后和用户输入的验证码内容进行比对,如果一样的话,则向指定的手机发送一条短信验证码,如果不一样的话,就返回验证码错误的信息.
6.短信验证码验证过程
1/先获取前端的参数,我们需要将json格式的数据利用dict_data = json.loads(json_data)
转化成字典格式.这里有一个注意点,就是我们在之前的时候都是先利用json_data = request.data
接收请求中的数据,记录下来并且转换为字符串,也就是引号里面是字典,我们需要讲字典转换过来,就利用上面的方式,一共需要两步,比较费时,我们还有一种更好的方式,那就是dict_data = request.json 或者reqeuts.get_json()
直接就将json格式,转成了字典的格式,是不是很方便,现在只需一步.
2/然后是参数为空校验,有一个方法:all([xx1,xx2,xx3....])
只有当列表里面的数据偶不为空才返回True,只要有一个为空那么返回False
3/校验手机的格式:利用正则判断
4/获取前端传递过来的参数中,我们利用图片验证码的编号到redis服务器中取出验证码的值,首先判断图片验证码是否过期(我们在存储图片验证码的时候设置了有效期),然后再利用取出的值和用户输入的验证码进行匹配,如果正确,往下进行,如果不正确返回错误信息.
5/如果验证码正确,删除redis中的验证码信息.
6/然后生成一个随机的短信验证码"%06d"%random.randint(0,999999)
,调用ccp发送短信,并且判断短信是否发送成功
7/将短信保存到redis中,可以将用户的手机号作为key,短信验证码作为值,然后再设置有效期.发送成功后,返回响应
这一部分,在前端中也可以实现,但是我们重点放在后端的代码实现上,毕竟前端不是主业,哈哈哈~~~~
7.注册功能实现
1/获取参数,这里我们直接就使用简单的方法dict_data = request.json
,我们需要的参数是手机号,短信验证码和用户的密码
2/校验参数:首先进行为空校验,如果有哪一项用户没有填写,直接返回错误信息,提示用户进行输入
3/利用获取得到的手机号,我们在redis中取出前面保存的验证码,
4/先判断短信验证码是否过期,因为之前设置了有效期,如果没有过期往下进行
5/将用户输入的短信验证码和我们从redis中取出的短信验证码进行比对,如果正确往下进行,如果不正确提示用户短信验证码输入不正确.
6/比对完毕后,将redis中的短信验证码进行删除.
7/上面所有的校验完成后,我们就可以将用户的信息保存到数据库中了,首先创建一个用户对象,然后将用户的密码,手机号和个性签名(此为非必填项,我们可以设置一个默认值"该用户很懒,什么都没有填写")多设置成创建的用户对象的属性.
8/将我们创建的对象添加到数据库中db.session.add(user)
,然后进行提交db.session.commit()
,然后返回响应.
8.登录功能实现
其实有了前面的功能实现之后,登录功能与其类似,我们仍然详细的解释一下,加深大家对这个案例的理解
1/首先是获取参数mobile = request.json.get("mobile")
,仍然是用这种简单的方式,直接返回我们需要的值.(这里我们需要的参数就只有手机号和用户的密码了)
2/校验参数:同样我们进行为空校验,同样是利用if not all([mobile,password]):
这种方式,失败返回用户输入信息不全的提示,成功之后往下进行.
3/通过用户输入的手机号,我们到数据库查询用户对象,如果用户不存在,那么提示用户输入有误,如果用户存在,我们将用户输入的密码与我们从数据库中提取出来的密码进行比对,密码不正确提示用户密码错误,密码正确我们往下进行.
4/将用户的登录信息保存到session中,session["user_id"] = user.id
然后我们还可以记录一下用户最后一次登录的时间user.last_login = datetime.now()
,我们之前配置为两天,也就是,只要用户不点击退出,那么用户的登录状态持续两天.
5/返回响应信息.
9.退出功能的实现
退出功能很好实现,用户点击了退出按钮后,前端将其绑定我们退出功能视图函数,我们直接将该用户的session信息删除掉就好了,然后返回响应:退出成功
session.pop("user_id",None)
就是将user_id的数据进行删除,如果没有,那么就返回None,而不会报错,导致系统崩掉.
10.CSRFProtect校验开启
我们这里先介绍非表单提交的校验过程,即ajax,还有一种是表单提交,比较简单,因为表单提交,只需要在表单中设置一个隐藏字段,设置csrf_cookie即可.
开启校验是为了防止CSRF攻击,我们利用校验机制,多加一层防护,更好的保护用户的安全.
首先我们需要使用CSRFProtect(app)
保护app,校验的请求方式是'POST', 'PUT', 'PATCH', 'DELETE'
,我们还需要设置SECRET_KEY
给csrf_token的值进行加密.这样在操作服务器资源的时候,服务器会校验cookie中的csrf_token和请求头中的csrf_token进行比较,只有匹配后才能通过.
使用请求钩子拦截所有的请求,通一的在cookie中设置csrf_token
1 #使用请求钩子拦截所有的请求,通一的在cookie中设置csrf_token 2 @app.after_request 3 def after_request(resp): 4 #调用系统方法,获取csrf_token,这是系统自动生成的,相对安全一点 5 csrf_token = generate_csrf() 6 7 #将csrf_token设置到cookie中 8 resp.set_cookie("csrf_token",csrf_token) 9 10 #返回响应 11 return resp
cookie如果不设置有效期,那么在浏览器退出的时候,就自动失效了
11.ajax
ajax一个前后台配合的技术,它可以让javascript发送http请求,与后台通信,获取数据和信息。ajax技术的原理是实例化xmlhttp对象,使用此对象与后台通信。jquery将它封装成了一个函数$.ajax(),我们可以直接用这个函数来执行ajax请求。
ajax需要在服务器环境下运行。
11.1$.ajax使用方法
常用参数:
1、url 请求地址
2、type 请求方式,默认是'GET',常用的还有'POST'
3、dataType 设置返回的数据格式,常用的是'json'格式,也可以设置为'html'
4、data 设置发送给服务器的数据
5、success 设置请求成功后的回调函数
6、error 设置请求失败后的回调函数
7、async 设置是否异步,默认值是'true',表示异步
以前的写法:
1$.ajax({ 2 url: '/change_data', 3 type: 'GET', 4 dataType: 'json', 5 data:{'code':300268} 6 success:function(dat){ 7 alert(dat.name); 8 }, 9 error:function(){ 10 alert('服务器超时,请重试!'); 11 } 12});
新的写法(推荐):
1$.ajax({ 2 url: '/change_data', 3 type: 'GET', 4 dataType: 'json', 5 data:{'code':300268} 6}) 7.done(function(dat) { 8 alert(dat.name); 9}) 10.fail(function() { 11 alert('服务器超时,请重试!'); 12});
11.2$.ajax的简写方式
$.ajax按照请求方式可以简写成$.get或者$.post方式
1$.get("/change_data", {'code':300268}, 2 function(dat){ 3 alert(dat.name); 4}); 5 6$.post("/change_data", {'code':300268}, 7 function(dat){ 8 alert(dat.name); 9});
与ajax相关的概念:
11.3同步和异步
现实生活中,同步指的是同时做几件事情,异步指的是做完一件事后再做另外一件事,程序中的同步和异步是把现实生活中的概念对调,也就是程序中的异步指的是现实生活中的同步,程序中的同步指的是现实生活中的异步。
11.4局部刷新和无刷新
ajax可以实现局部刷新,也叫做无刷新,无刷新指的是整个页面不刷新,只是局部刷新,ajax可以自己发送http请求,不用通过浏览器的地址栏,所以页面整体不会刷新,ajax获取到后台数据,更新页面显示数据的部分,就做到了页面局部刷新。
11.5数据接口
数据接口是后台程序提供的,它是一个url地址,访问这个地址,会对数据进行增、删、改、查的操作,最终会返回json格式的数据或者操作信息,格式也可以是text、xml等。
render_template('模板文件名',key=value)
是将数据携带到文件中进行展示,它会到templates文件夹中去寻找你需要的html页面
单例:单例模式是一个基础概念,在创建类的时候,第一次进入的其实不是__init__
方法,而是__new__
方法,进入new魔法方法是为了给下面创建一个对象.利用一个类每次返回相同的数值,每次都需要开辟内存,我们为了节约内存,需要选用单例方法,只开辟一次内存即可,以后每次需要,只需要返回第一次的值.
我们需要设置判断条件,进行判断是否是第一次进入。判断条件可以设置一个类属性,不能为实例属性是因为new方法是建立一个实例对象,实例对象都没有建,无法访问实例方法。我们只需要设置一个instence看它是否有值,先设置成None,节约内存。
自定义过滤器:有两种方法,我们重点掌握下面的这一种就可以了
1/先定义一个函数,这个函数就是要实现过滤功能的函数
2.然后使用app.add_template_filter('函数名','过滤器名称')
将我们自定义的过滤器添加到过滤器列表中
在一个 for 循环块中你可以访问这些特殊的变量:
变量 | 描述 |
loop.index | 当前循环迭代的次数(从 1 开始) |
loop.index0 | 当前循环迭代的次数(从 0 开始) |
下面两个功能是在首页中显示的,因此我们需要将下面两个功能写入到index.html中:
查询热门新闻排行,我们只需要将新闻根据点击量,按降序排列,然后查询前十条新闻,返回给前台进行渲染即可.
分类数据显示,我们需要到数据库中查询所有的分类信息,然后返回给前台,前端通过遍历分类信息,将分类数据显示到浏览器即可
首页的新闻展示,我们不能写在首页中了,因为我们在刷新或者滚动查看更多信息的时候,要获取更多的信息,如果是写在首页中,每次刷新都是显示一下首页中的数据,那么只能显示固定的几条新闻,因为浏览器有高度.我们需要的是新闻列表的部分,进行局部刷新,局部刷新的话,就需要使用ajax
下面是在新闻分类的时候查询的三种方式:
1 #判断新闻的分类是否为1 2 if cid == "1": 3 paginate = News.query.filter().order_by(News.create_time.desc()).paginate(page,per_page,False) 4 else: 5 paginate = News.query.filter(News.category_id == cid).order_by(News.create_time.desc()).paginate(page,per_page,False)
上面的挺好,但是不够灵活,因为在查询语句中条件写死了
1 # 改装,判断新闻的分类是否为1 2 filters = "" 3 if cid != "1": 4 filters = (News.category_id == cid) 5 paginate = News.query.filter(filters).order_by(News.create_time.desc()).paginate(page, per_page, False)
上面的这种方式比第一种灵活一点,filters = (News.category_id == cid)
,如果等于1,那么就是空的字符串,直接将所有的新闻按照时间进行降序排列,然后根据分页的条件,进行获取新闻
其中filters取出来的数据就是括号里面的语句,并不是一个括号和数据,下面我们做一个小实验:(上面是代码,下面是控制台输出的结果)
1filter = (123) 2print(filter) 3print(type(filter)) 4print(type((1))) 5----------------------------------------------- 6123 7<class 'int'> 8<class 'int'> 9
由此可见,括号里面有什么变量中存的就是什么,类型也一致.
1 filters = [] 2 if cid != "1": 3 filters.append(News.category_id == cid) 4 paginate = News.query.filter(*filters).order_by(News.create_time.desc()).paginate(page,per_page,False)
上面的这种方式,filter()中的*filters代表的是解包filter,如果没有数据,就是一个空的列表,有数据,就直接将查询语句拆出来放进过滤器中.这样做的好处是我们如果还有其他的条件,可以一起加进去,更加的灵活.
5.图片验证码验证的流程
1/前端中有专门的函数生成了验证码的一个编号,然后将这个编号提交给了后台去请求验证码的图片.
2/后台获取到此次验证码的编号和上一次验证码的编号(前端中生成验证码编号的函数每次生成编号的同时还会记录下上一次的编号,存下来,一起交给后台),
3/后台调用了captcha.generate_captcha()
来获取图片的验证码编号(这个编号我们不使用,我们使用的是前台发送过来的验证码编号),验证码的值和图片(二进制);
4/后台将图片保存在redis中,其中从前台获取的验证码编号作为key,验证码的值(就是图片验证码上的数字,字母)作为value,当然我们还要给这个键值对设置一个有效期
5/我们用从前台获取到的上一次验证码编号作为判断依据,如果有,那我们就将上一次的图片验证码在redis中保存的信息给删除,因为我们的redis服务器资源空间有限,防止有一种人(心里不爽或者恶作剧等重复点击验证码,使后台接收到过多的数据)恶性破坏,不能将每一次的验证码信息都保存下来,那样会撑爆服务器,我们就删除上一次,保存这一次的就好了.6/当然,凡是涉及到与服务器连接的情况,我们都要考虑连接失败的情况,不能连接失败,前台直接崩掉或者无响应吧,给用户的体验极其不好,因此,我们就try一下,如果错误就记录在日志中,并且向浏览器发送操作失败的相关信息.
7/最后就是重中之重了,我们将图片返回给前台response=make_response(image_data)
,这样我们还可以自定义返回的响应报文的信息,比如自定义状态码,制定会议文本类型,告诉浏览器这是图片response.headers["Content-Type"] = "image/png"
,让浏览器用渲染图片的方式去渲染这个数据,要不然浏览器默认的是文本格式,会给用户展示一坨乱码.将这些设置好后,直接return response
将响应报文返回就好了
8/前端申请发送短信验证码的时候也用到了图片验证码,因为只有当验证码输入正确的时候,后台才能委托第三方给用户发送短信
9/前端申请发送短信验证码的时候,带上第一步生成的验证码编号和用户输入的验证码内容
10/后台接收到了前端的申请之后,拿着编号去redis中取出验证码图片上的数字字母,然后和用户输入的验证码内容进行比对,如果一样的话,则向指定的手机发送一条短信验证码,如果不一样的话,就返回验证码错误的信息.
6.短信验证码验证过程
1/先获取前端的参数,我们需要将json格式的数据利用dict_data = json.loads(json_data)
转化成字典格式.这里有一个注意点,就是我们在之前的时候都是先利用json_data = request.data
接收请求中的数据,记录下来并且转换为字符串,也就是引号里面是字典,我们需要讲字典转换过来,就利用上面的方式,一共需要两步,比较费时,我们还有一种更好的方式,那就是dict_data = request.json 或者reqeuts.get_json()
直接就将json格式,转成了字典的格式,是不是很方便,现在只需一步.
2/然后是参数为空校验,有一个方法:all([xx1,xx2,xx3....])
只有当列表里面的数据偶不为空才返回True,只要有一个为空那么返回False
3/校验手机的格式:利用正则判断
4/获取前端传递过来的参数中,我们利用图片验证码的编号到redis服务器中取出验证码的值,首先判断图片验证码是否过期(我们在存储图片验证码的时候设置了有效期),然后再利用取出的值和用户输入的验证码进行匹配,如果正确,往下进行,如果不正确返回错误信息.
5/如果验证码正确,删除redis中的验证码信息.
6/然后生成一个随机的短信验证码"%06d"%random.randint(0,999999)
,调用ccp发送短信,并且判断短信是否发送成功
7/将短信保存到redis中,可以将用户的手机号作为key,短信验证码作为值,然后再设置有效期.发送成功后,返回响应
这一部分,在前端中也可以实现,但是我们重点放在后端的代码实现上,毕竟前端不是主业,哈哈哈~~~~
7.注册功能实现
1/获取参数,这里我们直接就使用简单的方法dict_data = request.json
,我们需要的参数是手机号,短信验证码和用户的密码
2/校验参数:首先进行为空校验,如果有哪一项用户没有填写,直接返回错误信息,提示用户进行输入
3/利用获取得到的手机号,我们在redis中取出前面保存的验证码,
4/先判断短信验证码是否过期,因为之前设置了有效期,如果没有过期往下进行
5/将用户输入的短信验证码和我们从redis中取出的短信验证码进行比对,如果正确往下进行,如果不正确提示用户短信验证码输入不正确.
6/比对完毕后,将redis中的短信验证码进行删除.
7/上面所有的校验完成后,我们就可以将用户的信息保存到数据库中了,首先创建一个用户对象,然后将用户的密码,手机号和个性签名(此为非必填项,我们可以设置一个默认值"该用户很懒,什么都没有填写")多设置成创建的用户对象的属性.
8/将我们创建的对象添加到数据库中db.session.add(user)
,然后进行提交db.session.commit()
,然后返回响应.
8.登录功能实现
其实有了前面的功能实现之后,登录功能与其类似,我们仍然详细的解释一下,加深大家对这个案例的理解
1/首先是获取参数mobile = request.json.get("mobile")
,仍然是用这种简单的方式,直接返回我们需要的值.(这里我们需要的参数就只有手机号和用户的密码了)
2/校验参数:同样我们进行为空校验,同样是利用if not all([mobile,password]):
这种方式,失败返回用户输入信息不全的提示,成功之后往下进行.
3/通过用户输入的手机号,我们到数据库查询用户对象,如果用户不存在,那么提示用户输入有误,如果用户存在,我们将用户输入的密码与我们从数据库中提取出来的密码进行比对,密码不正确提示用户密码错误,密码正确我们往下进行.
4/将用户的登录信息保存到session中,session["user_id"] = user.id
然后我们还可以记录一下用户最后一次登录的时间user.last_login = datetime.now()
,我们之前配置为两天,也就是,只要用户不点击退出,那么用户的登录状态持续两天.
5/返回响应信息.
9.退出功能的实现
退出功能很好实现,用户点击了退出按钮后,前端将其绑定我们退出功能视图函数,我们直接将该用户的session信息删除掉就好了,然后返回响应:退出成功
session.pop("user_id",None)
就是将user_id的数据进行删除,如果没有,那么就返回None,而不会报错,导致系统崩掉.
10.CSRFProtect校验开启
我们这里先介绍非表单提交的校验过程,即ajax,还有一种是表单提交,比较简单,因为表单提交,只需要在表单中设置一个隐藏字段,设置csrf_cookie即可.
开启校验是为了防止CSRF攻击,我们利用校验机制,多加一层防护,更好的保护用户的安全.
首先我们需要使用CSRFProtect(app)
保护app,校验的请求方式是'POST', 'PUT', 'PATCH', 'DELETE'
,我们还需要设置SECRET_KEY
给csrf_token的值进行加密.这样在操作服务器资源的时候,服务器会校验cookie中的csrf_token和请求头中的csrf_token进行比较,只有匹配后才能通过.
使用请求钩子拦截所有的请求,通一的在cookie中设置csrf_token
1 #使用请求钩子拦截所有的请求,通一的在cookie中设置csrf_token 2 @app.after_request 3 def after_request(resp): 4 #调用系统方法,获取csrf_token,这是系统自动生成的,相对安全一点 5 csrf_token = generate_csrf() 6 7 #将csrf_token设置到cookie中 8 resp.set_cookie("csrf_token",csrf_token) 9 10 #返回响应 11 return resp
cookie如果不设置有效期,那么在浏览器退出的时候,就自动失效了
11.ajax
ajax一个前后台配合的技术,它可以让javascript发送http请求,与后台通信,获取数据和信息。ajax技术的原理是实例化xmlhttp对象,使用此对象与后台通信。jquery将它封装成了一个函数$.ajax(),我们可以直接用这个函数来执行ajax请求。
ajax需要在服务器环境下运行。
11.1$.ajax使用方法
常用参数:
1、url 请求地址
2、type 请求方式,默认是'GET',常用的还有'POST'
3、dataType 设置返回的数据格式,常用的是'json'格式,也可以设置为'html'
4、data 设置发送给服务器的数据
5、success 设置请求成功后的回调函数
6、error 设置请求失败后的回调函数
7、async 设置是否异步,默认值是'true',表示异步
以前的写法:
1$.ajax({ 2 url: '/change_data', 3 type: 'GET', 4 dataType: 'json', 5 data:{'code':300268} 6 success:function(dat){ 7 alert(dat.name); 8 }, 9 error:function(){ 10 alert('服务器超时,请重试!'); 11 } 12});
新的写法(推荐):
1$.ajax({ 2 url: '/change_data', 3 type: 'GET', 4 dataType: 'json', 5 data:{'code':300268} 6}) 7.done(function(dat) { 8 alert(dat.name); 9}) 10.fail(function() { 11 alert('服务器超时,请重试!'); 12});
11.2$.ajax的简写方式
$.ajax按照请求方式可以简写成$.get或者$.post方式
1$.get("/change_data", {'code':300268}, 2 function(dat){ 3 alert(dat.name); 4}); 5 6$.post("/change_data", {'code':300268}, 7 function(dat){ 8 alert(dat.name); 9});
与ajax相关的概念:
11.3同步和异步
现实生活中,同步指的是同时做几件事情,异步指的是做完一件事后再做另外一件事,程序中的同步和异步是把现实生活中的概念对调,也就是程序中的异步指的是现实生活中的同步,程序中的同步指的是现实生活中的异步。
11.4局部刷新和无刷新
ajax可以实现局部刷新,也叫做无刷新,无刷新指的是整个页面不刷新,只是局部刷新,ajax可以自己发送http请求,不用通过浏览器的地址栏,所以页面整体不会刷新,ajax获取到后台数据,更新页面显示数据的部分,就做到了页面局部刷新。
11.5数据接口
数据接口是后台程序提供的,它是一个url地址,访问这个地址,会对数据进行增、删、改、查的操作,最终会返回json格式的数据或者操作信息,格式也可以是text、xml等。