登陆与注册

注册功能:

流程: 填写用户名,邮箱,获取邮箱验证码,填入密码 单击<下一步>按钮,完成注册!

1.获取邮箱验证码(具体步骤分析):

1.利用ajax 往后台传入邮箱,

2.后台表单验证,邮箱填写是否正确,

  => 成功,则继续3,

  => 错误,写入错误信息,页面输出错误提示!

3.查询sendcode 数据库表,查询该邮箱是否是第一次发送验证码

  => 第一次发送: 数据表中插入记录 (邮箱,发送次数1,邮箱验证码,状态1,有效时间:当前时间-1小时)

  => 否则: 从数据表中查询出该邮箱验证码发送次数,

      => 如果 >5 则,再判断上次发送时间+1小时 是否大于当前时间!

          => 大于当前时间,则发送验证码,修改数据表记录 (发送次数+1,邮箱验证码,有效时间)

=> 小于当前时间,则为多次恶意发送,不操作,写入错误信息,需要等待1小时,

=> 如果<5 则直接发送验证码,修改数据表记录 (发送次数+1,邮箱验证码,有效时间)

4.json信息返回。

  1. import json
  2. import datetime
  3.  
  4. from core.request_handle import BaseHandler
  5. # 公共方法
  6. from backend import commons,message
  7. # 数据库查询
  8. from models.sendcode import Insert,QueryCounts,Update
  9. from models.userinfo import InsertUser
  10. # 表单验证
  11. from forms.account import emailForm,registerForm
  12.  
  13. class SendEmailCodeHandler(BaseHandler):
  14. def post(self):
  15. obj = emailForm()
  16. val, success_info, error_info = obj.check_value(self)
  17. if not error_info:
  18. email = [success_info['email']]
  19. now = datetime.datetime.now()
  20. que = QueryCounts()
  21. ret = que.checkstime(email)
  22. if ret:
  23. if ret['stime'] < 5:
  24. UpdateCode(email)
  25. else:
  26. if (ret['ctime'] + datetime.timedelta(hours=1)) > now :
  27. error_info['stime'] = '验证码发送次数过多,请1小时后再发送'
  28. else:
  29. UpdateCode(email)
  30. else:
  31. InsertCode(email)
  32. data = {'success_info': success_info, 'error_info': error_info}
  33. self.write(json.dumps(data))
  34.  
  35. # 第一次发送code 数据表添加
  36. def InsertCode(email):
  37. code = commons.random_code() # 生成4位随机验证码(字母+数字)
  38. message.send_email(email, code)
  39. ins = Insert()
  40. ins.insertcode(email, code)
  41. print(email, code)
  42.  
  43. # 多次发送, 数据表修改
  44. def UpdateCode(email):
  45. code = commons.random_code()
  46. message.send_email(email, code)
  47. update = Update()
  48. update.updatestime(email, code)
  49. print(email, code)

SendEmailCodeHandler

  1. import random
  2. import string
  3.  
  4. def random_code():
  5. code = ''.join(random.sample(string.ascii_letters + string.digits, 4))
  6. return code

commons.py 公共方法:生成4位验证码

  1. //注册:生成邮箱验证码:
  2. $('#email-code').on('click',function () {
  3. var email = $('#re-email').val();
  4. $.ajax({
  5. url:'/email-code',
  6. type: 'POST',
  7. dataType: 'json',
  8. data:{'email':email},
  9. success: function(obj){
  10. if( isEmptyObject(obj['error_info']) ){
  11. $('.register-error').text('');
  12. $('.ac-code').addClass('hide');
  13. $('.ac-time').removeClass('hide');
  14. timechange();
  15. }
  16. $.each(obj['error_info'],function (k,v) {
  17. $('.register-error').text(v);
  18. return false;
  19. });
  20. }
  21. })
  22. });

前端JQ:发送验证码

2.验证码发送过程: 页面倒计时

原理:设计计时器,每1秒修改1次text值, 时间小于0时,取消计时器

  1. //邮箱验证码倒计时:
  2. function timechange() {
  3. var time = 60;
  4. var interval = setInterval(function () {
  5. if(time <= 0 ){
  6. $('.ac-time').addClass('hide');
  7. $('.ac-code').removeClass('hide');
  8. clearInterval(interval);
  9. }
  10. time = time - 1;
  11. var temp = "已发送("+time+"s)";
  12. $('.ac-time').text(temp)
  13. },1000)
  14. }

前端JQ:倒计时

3.帐号注册 

1.JQ获取前台输入表单内容

2.ajax传输给后台,建立表单验证

  => 正确,继续3

  => 错误,返回错误信息,页面显示错误信息

3.数据库中查询用户邮箱是否存在

  => 不存在,插入数据库,继续4

  => 存在,返回错误信息,页面显示

4.注册账号完成,写入session,注册完成页面刷新实现自动登录

  1. //注册帐号
  2. $('#register-next').on('click',function () {
  3. registerUser();
  4. clearinput();
  5. });
  6. function registerUser(){
  7. var username= $('#re-username').val();
  8. var email = $('#re-email').val();
  9. var code = $('#re-code').val();
  10. var password = $('#re-password').val();
  11. $.ajax({
  12. url:'/register',
  13. type: 'POST',
  14. dataType: 'json',
  15. data:{'username':username,'email':email,'code':code,'password':password},
  16. success: function(obj){
  17. if( isEmptyObject(obj['error_info']) ){
  18. $('.register-error').text('');
  19. //跳转(隐藏输入页面):
  20. $('.login-block').addClass('hide');
  21. $('.register-info').removeClass('hide');
  22. }
  23. $.each(obj['error_info'],function (k,v) {
  24. $('.register-error').text(v);
  25. return false;
  26. });
  27. }
  28. })
  29. }

前端JQ:获取输入值

  1. class RegisterUserHandler(BaseHandler):
  2. def post(self):
  3. obj = registerForm()
  4. val, success_info, error_info = obj.check_value(self)
  5. print(val, success_info, error_info)
  6. if not error_info:
  7. ins = InsertUser()
  8. ins.inser(success_info['username'],success_info['password'],success_info['email'])
  9. self.session['is_login'] = 1
  10. self.session['username'] = success_info['username']
  11. data = {'success_info': success_info, 'error_info': error_info}
  12. self.write(json.dumps(data))

RegisterUserHandler

表单错误时,页面信息提示(效果图):

登录功能:

原理:与注册功能类似

1.图片验证码:点击验证码,自动更改

验证码为后台生成的图片,每点击一次,src地址后面 多加?实现切换

  1. <div class="inp-block">
  2. <input type="text" name="phoneregister" placeholder="请输入验证码" id="u-code" class="phonenum" autocomplete="off"/>
  3. <img src="/check_code" onclick='ChangeCode();' id='imgCode' class="codeimg">
  4. </div>
  1. //注册页面验证码切换
  2. function ChangeCode() {
  3. var code = document.getElementById('imgCode');
  4. code.src += '?';
  5. }

前端JQ:点击改变验证码

  1. //登录,确定按钮:(邮箱形式)
  2. $('.login-btn').on('click',function () {
  3. var username = $('#username').val();
  4. var password = $('#password').val();
  5. var code = $('#u-code').val();
  6. var remember = 0;
  7. if($('#phone-remember').is(':checked')){
  8. remember = 1;
  9. }
  10. $.ajax({
  11. url:'/userlogin',
  12. type: 'POST',
  13. dataType: 'text',
  14. data:{'username':username,'password':password,'code':code,'remember':remember},
  15. success: function(data, statusText, xmlHttpRequest){
  16. obj = JSON.parse(data);
  17. if( isEmptyObject(obj['error_info']) ){
  18. $('.login-error').text('');
  19. window.location.reload();
  20. }
  21. $.each(obj['error_info'],function (k,v) {
  22. $('.login-error').text(v);
  23. return false;
  24. });
  25. }
  26. })
  27. });

前端JQ:登录按钮

  1. import io
  2.  
  3. from backend.utils import check_code
  4. from core.request_handle import BaseHandler
  5.  
  6. class CheckcodeHandler(BaseHandler):
  7. def get(self, *args, **kwargs):
  8. mstream = io.BytesIO()
  9. img, code = check_code.create_validate_code()
  10. self.session['code'] = code
  11. print(self.session['code'])
  12. img.save(mstream, "GIF")
  13. self.write(mstream.getvalue())

CheckcodeHandler

  1. import tornado.ioloop
  2. import tornado.web
  3. import json
  4. import os
  5. import sys
  6.  
  7. from core.request_handle import BaseHandler
  8. from models.userinfo import QueryUser
  9. from forms.account import LoginForm
  10.  
  11. class LoginUserHandler(BaseHandler):
  12. def post(self, *args, **kwargs):
  13. obj = LoginForm()
  14. val, success_info, error_info = obj.check_value(self)
  15. print(val, success_info, error_info)
  16. if not error_info:
  17. if self.session['code'].upper() == success_info['code'].upper():
  18. query = QueryUser()
  19. ret = query.checkusername(success_info['username'],success_info['password'])
  20. if ret:
  21. self.session['is_login'] = 1
  22. self.session['username'] = ret['username']
  23. self.session['user_id'] = ret['user_id']
  24. else:
  25. error_info = { 'username' : '用户名或密码错误'}
  26. else:
  27. error_info = {'code': '验证码错误'}
  28. data = { 'success_info': success_info , 'error_info': error_info }
  29. self.write(json.dumps(data))

LoginUserHandler

  1. #!/usr/bin/env python
  2. #coding:utf-8
  3.  
  4. import random
  5. from PIL import Image, ImageDraw, ImageFont, ImageFilter
  6.  
  7. _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
  8. _upper_cases = _letter_cases.upper() # 大写字母
  9. _numbers = ''.join(map(str, range(3, 10))) # 数字
  10. init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
  11.  
  12. def create_validate_code(size=(120, 30),
  13. chars=init_chars,
  14. img_type="GIF",
  15. mode="RGB",
  16. bg_color=(255, 255, 255),
  17. fg_color=(0, 0, 255),
  18. font_size=18,
  19. font_type="Monaco.ttf",
  20. length=4,
  21. draw_lines=True,
  22. n_line=(1, 2),
  23. draw_points=True,
  24. point_chance = 2):
  25. '''
  26. @todo: 生成验证码图片
  27. @param size: 图片的大小,格式(宽,高),默认为(120, 30)
  28. @param chars: 允许的字符集合,格式字符串
  29. @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
  30. @param mode: 图片模式,默认为RGB
  31. @param bg_color: 背景颜色,默认为白色
  32. @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
  33. @param font_size: 验证码字体大小
  34. @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
  35. @param length: 验证码字符个数
  36. @param draw_lines: 是否划干扰线
  37. @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
  38. @param draw_points: 是否画干扰点
  39. @param point_chance: 干扰点出现的概率,大小范围[0, 100]
  40. @return: [0]: PIL Image实例
  41. @return: [1]: 验证码图片中的字符串
  42. '''
  43.  
  44. width, height = size # 宽, 高
  45. img = Image.new(mode, size, bg_color) # 创建图形
  46. draw = ImageDraw.Draw(img) # 创建画笔
  47.  
  48. def get_chars():
  49. '''生成给定长度的字符串,返回列表格式'''
  50. return random.sample(chars, length)
  51.  
  52. def create_lines():
  53. '''绘制干扰线'''
  54. line_num = random.randint(*n_line) # 干扰线条数
  55.  
  56. for i in range(line_num):
  57. # 起始点
  58. begin = (random.randint(0, size[0]), random.randint(0, size[1]))
  59. #结束点
  60. end = (random.randint(0, size[0]), random.randint(0, size[1]))
  61. draw.line([begin, end], fill=(0, 0, 0))
  62.  
  63. def create_points():
  64. '''绘制干扰点'''
  65. chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
  66.  
  67. for w in range(width):
  68. for h in range(height):
  69. tmp = random.randint(0, 100)
  70. if tmp > 100 - chance:
  71. draw.point((w, h), fill=(0, 0, 0))
  72.  
  73. def create_strs():
  74. '''绘制验证码字符'''
  75. c_chars = get_chars()
  76. strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
  77.  
  78. font = ImageFont.truetype(font_type, font_size)
  79. font_width, font_height = font.getsize(strs)
  80.  
  81. draw.text(((width - font_width) / 3, (height - font_height) / 3),
  82. strs, font=font, fill=fg_color)
  83.  
  84. return ''.join(c_chars)
  85.  
  86. if draw_lines:
  87. create_lines()
  88. if draw_points:
  89. create_points()
  90. strs = create_strs()
  91.  
  92. # 图形扭曲参数
  93. params = [1 - float(random.randint(1, 2)) / 100,
  94. 0,
  95. 0,
  96. 0,
  97. 1 - float(random.randint(1, 10)) / 100,
  98. float(random.randint(1, 2)) / 500,
  99. 0.001,
  100. float(random.randint(1, 2)) / 500
  101. ]
  102. img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲
  103.  
  104. img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)
  105.  
  106. return img, strs

check_code.py

表单验证

设计思想: 无论表单通过form形式,或者ajax形式,发送数据给后端,我们都要先通过:某些规则进行过滤和验证,再对其进行数据的插入,修改等操作

实现思路:1,有form表单数据来临时,初始化一个表单类,传入self;

     2,自动获取form上数据字段,self.get_arguments(name),name与前端input标签的name名字一一对应;

     3,通过表单类,调用检测方法 check_value(),

     4,如果form表单数据,符合自定义的规则,以字典形式返回数据,{ ‘前端input的name值’:‘input的value值’ } =》 存储在表单类的 self._value_dict 中

     5,如果form表单数据,不符合自定义的规则,以字典形式返回错误信息,{ ‘前端input的name值’:‘错误信息’ } =》 存储在表单类的 self._error_dict 中

1. 定义一个表单类 LoginForm ,__init__方法中对象名为 前端input标签的name值

required 是否可以为空值, error_dict 定义错误信息输出内容

  1. class LoginForm(BaseForm):
  2. def __init__(self):
  3. self.username = UsernameField(required=True,error_dict={'required':'用户名不能为空' , 'valid':'用户名错误'})
  4. self.password = PasswordField(required=True,error_dict={'required':'密码不能为空' , 'valid':'用户名或密码错误'})
  5. self.code = CodeField(required=True,error_dict={'required':'验证码不能为空' , 'valid':'验证码错误'})
  6. self.remember = CheckBoxField(required=False,error_dict={'valid':'格式错误'})
  7. super(LoginForm, self).__init__()

2. 继承BaseForm类,该类初始化了,当前Form表单共用的最后返回数据和错误信息的字典对象

  1. class BaseForm():
  2. def __init__(self):
  3. self._value_dict = {}
  4. self._error_dict = {}
  5. self._valid_status = True
  6.  
  7. def check_value(self):
  8. ....

check_value() 执行获取前端输入的数据,self.get_argument(xxxx) 并且根据每个input标签定义的规则去验证数据的正确性 (上文的UsernameField,PasswordField..等)

通过self.__dict__循环获取LoginForm的成员对象,调用Field的validate()方法,验证Form表单中每一个的值。验证正确,信息存储在self._value_dict 中, 错误信息存储在self._error_dict 中

  1. def check_value(self,handler):
  2. for key,regular in self.__dict__.items():
  3. input_value = handler.get_argument(key,None)
  4. regular.validate(key,input_value)
  5. if regular.is_valid:
  6. self._value_dict[key] = regular.value
  7. else:
  8. self._error_dict[key] = regular.error
  1. class BaseForm():
  2. def __init__(self):
  3. self._value_dict = {}
  4. self._error_dict = {}
  5. self._valid_status = True
  6.  
  7. def check_value(self,handler):
  8. for key,regular in self.__dict__.items():
  9. if key.startswith('_'):
  10. continue
  11. if type(regular) == fields.CheckBoxField:
  12. input_value = handler.get_arguments(key) # checkbox取值
  13. elif type(regular) == fields.FileField:
  14. file_list = handler.request.files.get(key,[]) # 文件对象
  15. input_value = []
  16. for item in file_list:
  17. input_value.append(item['filename'])
  18. else:
  19. input_value = handler.get_argument(key,None)
  20.  
  21. regular.validate(key,input_value)
  22.  
  23. if regular.is_valid:
  24. self._value_dict[key] = regular.value
  25. else:
  26. self._error_dict[key] = regular.error
  27. self._valid_status = False
  28. return self._valid_status

BaseForm 完整代码

3. Field 自定义的规则类

为前端input标签,定义不同的验证规则(正则表达式),验证用户输入的数据

  1. class PasswordField(Field):
  2. REGULAR = "[0-9 | A-Z | a-z]{6,16}"
  3. def __init__(self,required=True,error_dict=None):
  4.  
  5. self.error_dict = {} #错误信息
  6. if error_dict:
  7. self.error_dict.update(error_dict) #用户自定义的错误信息
  8.  
  9. self.required = required
  10. super(PasswordField, self).__init__()

继承父类Field,初始化存储信息的成员

  1. class Field:
  2.  
  3. def __init__(self):
  4. self.is_valid = False # 验证规则是否通过,默认False
  5. self.name = None
  6. self.value = None # 获取的前端input值
  7. self.error = None
  8.  
  9. def validate(self, name, input_value):
  10. ...

执行validate()方法,

  1.先判断该值是否允许为空?,

    => 可以为空,验证通过,self.value = input输入值

    => 不可以为空, 判断 input输入值 是否为空?

        => input输入值为空,self.error = 定义的错误信息(required)

        => 不为空,继续正则表达式判断,re.match(REGULAR,input_value)

            => 正则通过,self.value = input输入值, self.is_valid = True

            => 正则不通过, self.error = 定义的错误信息(valid)

  1. class Field:
  2.  
  3. def __init__(self):
  4. self.is_valid = False
  5. self.name = None
  6. self.value = None
  7. self.error = None
  8.  
  9. def validate(self, name, input_value):
  10. self.name = name
  11.  
  12. if not self.required: # 可以为空
  13. self.value = input_value
  14. self.is_valid = True
  15. else:
  16. if not input_value:
  17. if self.error_dict.get('required', None):
  18. self.error = self.error_dict['required']
  19. else:
  20. self.error = '%s is requires ' % (name)
  21. else:
  22. val = re.match(self.REGULAR, input_value)
  23. if not val:
  24. if self.error_dict.get('valid', None):
  25. self.error = self.error_dict['valid']
  26. else:
  27. self.error = '%s is valid ' % (name)
  28. else:
  29. self.value = input_value
  30. self.is_valid = True

Field 完整代码

自定义的Field 供参考:

  1. class CheckBoxField(Field):
  2. REGULAR = "^\d+$"
  3.  
  4. def __init__(self,required=True,error_dict=None):
  5.  
  6. self.error_dict = {}
  7. if error_dict:
  8. self.error_dict.update(error_dict)
  9. self.required = required
  10. super(CheckBoxField, self).__init__()
  11.  
  12. def validate(self,name,input_value):
  13. if not self.required:
  14. self.value = input_value
  15. self.is_valid = True
  16. else:
  17. if not input_value:
  18. if self.error_dict.get('required',None):
  19. self.error = self.error_dict['required']
  20. else:
  21. self.error = '%s is requires '%(name)
  22. else:
  23. if isinstance(name, list):
  24. self.is_valid = True
  25. self.value = input_value
  26. else:
  27. if self.error_dict.get('valid', None):
  28. self.error = self.error_dict['valid']
  29. else:
  30. self.error = "%s is invalid" % name

CheckBoxField

  1. class FileField(Field):
  2. REGULAR = "^(\w+\.jpg)|(\w+\.jpeg)|(\w+\.gif)|(\w+\.png)$"
  3.  
  4. def __init__(self, required=True, error_dict=None):
  5. self.error_dict = {}
  6. if error_dict:
  7. self.error_dict.update(error_dict)
  8. self.required = required
  9. self.value = []
  10. self.success_file_name_list = []
  11. super(FileField, self).__init__()
  12.  
  13. def validate(self, name, all_file_name_list):
  14. self.name = name
  15. if not self.required:
  16. self.value = all_file_name_list
  17. else:
  18. if not all_file_name_list:
  19. self.is_valid = False
  20. if self.error_dict.get('required', None):
  21. self.error = self.error_dict['required']
  22. else:
  23. self.error = '%s is requires ' % (name)
  24. else:
  25. for file_name in all_file_name_list:
  26. if not file_name or not file_name.strip():
  27. if self.error_dict.get('required', None):
  28. self.error = self.error_dict['required']
  29. else:
  30. self.error = "%s is required" % name
  31. self.is_valid = False
  32. break
  33. else:
  34. val = re.match(FileField.REGULAR, file_name)
  35. if not val:
  36. self.is_valid = False
  37. if self.error_dict.get('valid', None):
  38. self.error = self.error_dict['valid']
  39. else:
  40. self.error = '%s is valid ' % (name)
  41. break
  42. else:
  43. self.value.append(file_name)

FileField

  1. class EmailField(Field):
  2.  
  3. REGULAR = "^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$"
  4.  
  5. def __init__(self, required=True, error_dict=None):
  6.  
  7. self.error_dict = {} # 错误信息
  8. if error_dict:
  9. self.error_dict.update(error_dict) # 用户自定错误信息
  10. self.required = required
  11. super(EmailField, self).__init__()

EmailField (input类型为text通用)

Session

设计思想: 利用Cookie 自定义一个Session来存储每个用户信息

实现思路: 1. Session信息设计成一个大字典,key对应用户唯一识别加密串,value对应空字典{}存储用户信息,存放在服务端上。

      2. 为用户请求生成一个唯一加密串,写入到Cookie信息中,Session_id = 加密串,用于区分每一个用户。

      3. 当用户请求到来时,获取该请求的Cookie信息,判断是否存在Session_id(用户唯一识别加密串)?

      => 如果存在Session_id 并且在 Session大字典中找到相同的 key,记录Session_id

      => 其他情况下一律,生成加密串,写入到Cookie中,同时写入到 Session大字典中

  1. class CacheSession():
  2. session_id = "__balabala__" # Cookie中为Session存储的名字
  3. session_container = {} # Session大字典
  4.  
  5. def __init__(self,handler):
  6. self.handler = handler
  7. client_random_str = self.handler.get_cookie(CacheSession.session_id,None)
  8. if client_random_str and client_random_str in CacheSession.session_container:
  9. self.random_str = client_random_str
  10. else:
  11. self.random_str = self.__container__random__str()
  12. CacheSession.session_container[self.random_str] = {}
  13.  
  14. expires_time = time.time() + config.SESSION_EXPIRES
  15. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  16.  
  17. def __container__random__str(self): # 生成加密串
  18. hash = hashlib.md5()
  19. hash.update(bytes(str(time.time()), encoding='utf-8'))
  20. random_str = hash.hexdigest()
  21. return random_str

利用方法:1. 当用户请求到达每个Handler时,我们都需要先实例化一个CacheSession(),

     2. 此时我们可以定义一个父类BaseHandler,initialize() 方法中写入要执行代码,

  1. import tornado.web
  2. form session import SessionFactory
  3.  
  4. class BaseHandler(tornado.web.RequestHandler):
  5. def initialize(self):
  6. self.session = SessionFactory.get_session_obj(self)

可以看到,我们在session.py中定义了一个新的类SessionFactory,用来选择合适的方法,初始化Session,

该类通过读取配置文件config中的SESSION_TYPE选择适合的Session类进行初始化,并且返回一个Session对象,该对象最终存储在 self.session中。

  1. class SessionFactory():
  2.  
  3. @staticmethod
  4. def get_session_obj(handler):
  5. if config.SESSION_TYPE == 'cache':
  6. obj = CacheSession(handler)
  7. elif config.SESSION_TYPE == 'memcached':
  8. obj = MemcachedSession(handler)
  9. elif config.SESSION_TYPE == 'redis':
  10. obj = RedisSession(handler)
  11. return obj

Handler 中使用Session

  1. class LoginUserHandler(BaseHandler):
  2. def post(self, *args, **kwargs):
  3. self.session['is_login'] = 1
  4. self.write('ok')

1.缓存Session

  1. import time
  2. import hashlib
  3. import config
  4. import memcache
  5. import json
  6. import redis
  7.  
  8. class CacheSession():
  9. session_id = "__balabala__"
  10. session_container = {}
  11.  
  12. def __init__(self,handler):
  13. self.handler = handler
  14. client_random_str = self.handler.get_cookie(CacheSession.session_id,None)
  15. if client_random_str and client_random_str in CacheSession.session_container:
  16. self.random_str = client_random_str
  17. else:
  18. self.random_str = self.__container__random__str()
  19. CacheSession.session_container[self.random_str] = {}
  20.  
  21. expires_time = time.time() + config.SESSION_EXPIRES
  22. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  23.  
  24. def __container__random__str(self):
  25. hash = hashlib.md5()
  26. hash.update(bytes(str(time.time()), encoding='utf-8'))
  27. random_str = hash.hexdigest()
  28. return random_str
  29.  
  30. def __setitem__(self,key,value):
  31. CacheSession.session_container[self.random_str][key] = value
  32.  
  33. def __getitem__(self,key):
  34. result = CacheSession.session_container[self.random_str].get(key,None)
  35. return result
  36.  
  37. def __delitem__(self,key):
  38. if key in CacheSession.session_container[self.random_str]:
  39. del CacheSession.session_container[self.random_str][key]

2.memcache session

  1. conn = memcache.Client(['127.0.0.1:11210'], debug=True)
  2.  
  3. class MemcachedSession():
  4. session_id = "__balabala__"
  5.  
  6. def __init__(self,handler):
  7. self.handler = handler
  8. client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None)
  9. if client_random_str and conn.get(client_random_str):
  10. self.random_str = client_random_str
  11. else:
  12. self.random_str = self.__container__random__str()
  13. conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES)
  14.  
  15. conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)
  16.  
  17. expires_time = time.time() + config.SESSION_EXPIRES
  18. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  19.  
  20. def __container__random__str(self):
  21. hash = hashlib.md5()
  22. hash.update(bytes(str(time.time()), encoding='utf-8'))
  23. random_str = hash.hexdigest()
  24. return random_str
  25.  
  26. def __setitem__(self,key,value):
  27. ret = conn.get(self.random_str)
  28. ret_dict = json.loads(ret)
  29. ret_dict[key] = value
  30. conn.set(self.random_str,json.dumps(ret_dict),config.SESSION_EXPIRES)
  31.  
  32. def __getitem__(self,key):
  33. ret = conn.get(self.random_str)
  34. ret_dict = json.loads(ret)
  35. result = ret_dict.get(key,None)
  36. return result
  37.  
  38. def __delitem__(self, key):
  39. ret = conn.get(self.random_str)
  40. ret_dict = json.loads(ret)
  41. del ret_dict[key]
  42. conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)

3.radis session

  1. pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
  2. r = redis.Redis(connection_pool=pool)
  3.  
  4. class RedisSession():
  5. session_id = "__balabala__"
  6.  
  7. def __init__(self, handler):
  8. self.handler = handler
  9. client_random_str = self.handler.get_cookie(RedisSession.session_id, None)
  10. if client_random_str and r.exists(client_random_str):
  11. self.random_str = client_random_str
  12. else:
  13. self.random_str = self.__container__random__str()
  14. r.hset(self.random_str, None, None)
  15.  
  16. r.expire(self.random_str,config.SESSION_EXPIRES)
  17.  
  18. expires_time = time.time() + config.SESSION_EXPIRES
  19. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  20.  
  21. def __container__random__str(self):
  22. hash = hashlib.md5()
  23. hash.update(bytes(str(time.time()), encoding='utf-8'))
  24. random_str = hash.hexdigest()
  25. return random_str
  26.  
  27. def __setitem__(self, key, value):
  28. if type(value) == dict:
  29. r.hset(self.random_str, key, json.dumps(value))
  30. else:
  31. r.hset(self.random_str, key, value)
  32.  
  33. def __getitem__(self, key):
  34. ret = r.hget(self.random_str,key)
  35. if ret :
  36. ret_str = str(ret,encoding='utf-8')
  37. try:
  38. result = json.loads(ret_str)
  39. except:
  40. result = ret_str
  41. return result
  42. else:
  43. return ret
  44.  
  45. def __delitem__(self, key):
  46. r.hdel(self.random_str, key)

需要注意的是:__setitem__,__getitem__和__delitem__  使用类似字典方式设置,访问,删除成员。

在缓存Session 中,他们的使用方法与字典差别不大。

在memcache 中,键值对key,value 都是以字符串的形式存储的,

在设置值前需要将value值通过json转换成字典形式,再对字典进行操作,操作完毕后,用json转换回字符串,存储回原来位置!

在redis 中,选用hash操作进行存储,如果待存储的value值为字典,需要先把value通过json转换成字符串,再存储在redis中,

获取某个key的value值时,由于hash中value是以bytes存储,需要先转换成str类型,再判断该key存储的是字典,还是普通字符串

Session所有的完整代码:

  1. #!/usr/bin/env python
  2. # -*-coding:utf-8 -*-
  3.  
  4. import time
  5. import hashlib
  6. import config
  7. import memcache
  8. import json
  9. import redis
  10.  
  11. class CacheSession():
  12. session_id = "__balabala__"
  13. session_container = {}
  14.  
  15. def __init__(self,handler):
  16. self.handler = handler
  17. client_random_str = self.handler.get_cookie(CacheSession.session_id,None)
  18. if client_random_str and client_random_str in CacheSession.session_container:
  19. self.random_str = client_random_str
  20. else:
  21. self.random_str = self.__container__random__str()
  22. CacheSession.session_container[self.random_str] = {}
  23.  
  24. expires_time = time.time() + config.SESSION_EXPIRES
  25. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  26.  
  27. def __container__random__str(self):
  28. hash = hashlib.md5()
  29. hash.update(bytes(str(time.time()), encoding='utf-8'))
  30. random_str = hash.hexdigest()
  31. return random_str
  32.  
  33. def __setitem__(self,key,value):
  34. CacheSession.session_container[self.random_str][key] = value
  35.  
  36. def __getitem__(self,key):
  37. result = CacheSession.session_container[self.random_str].get(key,None)
  38. return result
  39.  
  40. def __delitem__(self,key):
  41. if key in CacheSession.session_container[self.random_str]:
  42. del CacheSession.session_container[self.random_str][key]
  43.  
  44. conn = memcache.Client(['127.0.0.1:11210'], debug=True)
  45.  
  46. class MemcachedSession():
  47. session_id = "__balabala__"
  48.  
  49. def __init__(self,handler):
  50. self.handler = handler
  51. client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None)
  52. if client_random_str and conn.get(client_random_str):
  53. self.random_str = client_random_str
  54. else:
  55. self.random_str = self.__container__random__str()
  56. conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES)
  57.  
  58. conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)
  59.  
  60. expires_time = time.time() + config.SESSION_EXPIRES
  61. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  62.  
  63. def __container__random__str(self):
  64. hash = hashlib.md5()
  65. hash.update(bytes(str(time.time()), encoding='utf-8'))
  66. random_str = hash.hexdigest()
  67. return random_str
  68.  
  69. def __setitem__(self,key,value):
  70. ret = conn.get(self.random_str)
  71. ret_dict = json.loads(ret)
  72. ret_dict[key] = value
  73. conn.set(self.random_str,json.dumps(ret_dict),config.SESSION_EXPIRES)
  74.  
  75. def __getitem__(self,key):
  76. ret = conn.get(self.random_str)
  77. ret_dict = json.loads(ret)
  78. result = ret_dict.get(key,None)
  79. return result
  80.  
  81. def __delitem__(self, key):
  82. ret = conn.get(self.random_str)
  83. ret_dict = json.loads(ret)
  84. del ret_dict[key]
  85. conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)
  86.  
  87. pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
  88. r = redis.Redis(connection_pool=pool)
  89.  
  90. class RedisSession():
  91. session_id = "__balabala__"
  92.  
  93. def __init__(self, handler):
  94. self.handler = handler
  95. client_random_str = self.handler.get_cookie(RedisSession.session_id, None)
  96. if client_random_str and r.exists(client_random_str):
  97. self.random_str = client_random_str
  98. else:
  99. self.random_str = self.__container__random__str()
  100. r.hset(self.random_str, None, None)
  101.  
  102. r.expire(self.random_str,config.SESSION_EXPIRES)
  103.  
  104. expires_time = time.time() + config.SESSION_EXPIRES
  105. self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便后续定义过期时间!
  106.  
  107. def __container__random__str(self):
  108. hash = hashlib.md5()
  109. hash.update(bytes(str(time.time()), encoding='utf-8'))
  110. random_str = hash.hexdigest()
  111. return random_str
  112.  
  113. def __setitem__(self, key, value):
  114. if type(value) == dict:
  115. r.hset(self.random_str, key, json.dumps(value))
  116. else:
  117. r.hset(self.random_str, key, value)
  118.  
  119. def __getitem__(self, key):
  120. ret = r.hget(self.random_str,key)
  121. if ret :
  122. ret_str = str(ret,encoding='utf-8')
  123. try:
  124. result = json.loads(ret_str)
  125. except:
  126. result = ret_str
  127. return result
  128. else:
  129. return ret
  130.  
  131. def __delitem__(self, key):
  132. r.hdel(self.random_str, key)
  133.  
  134. class SessionFactory():
  135.  
  136. @staticmethod
  137. def get_session_obj(handler):
  138. if config.SESSION_TYPE == 'cache':
  139. obj = CacheSession(handler)
  140. elif config.SESSION_TYPE == 'memcached':
  141. obj = MemcachedSession(handler)
  142. elif config.SESSION_TYPE == 'redis':
  143. obj = RedisSession(handler)
  144. return obj

session.py

  1. #!/usr/bin/env python
  2. # -*-coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. from backend.session.session import SessionFactory
  8.  
  9. class BaseHandler(tornado.web.RequestHandler):
  10. def initialize(self):
  11. self.session = SessionFactory.get_session_obj(self)

request_handle.py

  1. #!/usr/bin/env python
  2. # -*-coding:utf-8 -*-
  3.  
  4. # Session类型:cache/redis/memcached
  5. SESSION_TYPE = "cache"
  6.  
  7. # Session超时时间(秒)
  8. SESSION_EXPIRES = 180

config.py 配置文件

分页

设计思路:与Tornado篇分页同理

前端:

  1. <div class="pagination">
  2. {% raw str_page%} #展示原生html
  3. </div>

前端页面

后台Url配置:

  1. (r"/index/(?P<page>\d*)", IndexHandler),

后台Handle:

  1. class IndexHandler(BaseHandler):
  2. def get(self, page):
  3. query = QueryContent()
  4. page_obj = pager.Pagiantion(page, all_item=query.queryCounts(), per_num=5) #page 当前页码 , all_item 总数据条数, per_num 每页显示条数
  5. str_page = page_obj.page_str('/index/')
  6.  
  7. self.render("index.html", str_page=str_page )

Pagiantion 分页类:

  1. class Pagiantion:
  2. def __init__(self,current_page,all_counts,per_num):
  3.  
  4. all_page, c = divmod(all_counts, per_num)
  5. if c>0:
  6. all_page += 1
  7. self.all_page = all_page
  8.  
  9. try:
  10. current_page = int(current_page)
  11. except:
  12. current_page = 1
  13. if current_page < 1:
  14. current_page = 1
  15. self.current_page = current_page
  16.  
  17. def page_str(self,base_url):
  18. if self.all_page < 10:
  19. s = 1
  20. t = self.all_page
  21. else:
  22. if self.current_page < 7:
  23. s = 1
  24. t = 10
  25. else:
  26. if self.all_page - self.current_page >= 4 :
  27. s = self.current_page - 3
  28. t = self.current_page + 4
  29. else:
  30. s = self.all_page - 6
  31. t = self.all_page
  32. list_page = []
  33.  
  34. # 上一页
  35. if self.current_page != 1:
  36. pre_page = "<a href='%s%s' class='pageedg'>上一页</a>" % (base_url, self.current_page - 1,)
  37. list_page.append(pre_page)
  38.  
  39. # 页码生成
  40. # 生成 1 , ... ,
  41. if( self.current_page >= 7 ):
  42. temp = "<a class='pageNum' href='%s%s'>%s</a>" % (base_url, 1, 1)
  43. list_page.append(temp)
  44. temp = "<span class='ignore' >...</span>"
  45. list_page.append(temp)
  46.  
  47. for i in range(s, t + 1):
  48. if i == self.current_page:
  49. temp = "<span class='active-page' href='%s%s'>%s</span>" % (base_url,i, i)
  50. else:
  51. temp = "<a href='%s%s' class='pageNum'>%s</a>" % (base_url , i, i)
  52. list_page.append(temp)
  53.  
  54. # 下一页
  55. if self.current_page < self.all_page:
  56. next_page = "<a href='%s%s' class='pageedg'>下一页</a>" % (base_url, self.current_page + 1,)
  57. list_page.append(next_page)
  58.  
  59. # 数据拼接 返回
  60. str_page = ''.join(list_page) # 列表连接成为字符串
  61. return str_page

Pagiantion 分页类

  1. /*分页*/
  2. .dig-page-block{
  3. width: 640px;
  4. height: 40px;
  5. /*border: 1px solid red;*/
  6. float: left;
  7. margin:20px 0 60px 0;
  8. }
  9. .dig-page {
  10. width:630px;
  11. height: 38px;
  12. }
  13. .dig-page a.pageNum{
  14. display: inline-block;
  15. min-width:34px;
  16. height: 34px;
  17. color: #369;
  18. line-height: 34px;
  19. text-align: center;
  20. border: 1px solid #e1e1e1;
  21. border-radius: 5px 5px;
  22. margin-right:6px;
  23. text-decoration: none;
  24. font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
  25. }
  26. .dig-page a.pageNum:hover{
  27. color: white;color: #fff;
  28. background-color: #369;
  29. border: 1px solid #369;
  30. }
  31. /*页码中文字体*/
  32. .dig-page a.pageedg{
  33. display: inline-block;
  34. width: 77px;
  35. height: 34px;
  36. color: #369;
  37. line-height: 34px;
  38. text-align: center;
  39. border: 1px solid #e1e1e1;
  40. border-radius: 5px 5px;
  41. margin-right:6px;
  42. text-decoration: none;
  43. font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
  44. }
  45. .dig-page a.pageedg:hover{
  46. color: #fff;
  47. background-color: #369;
  48. border: 1px solid #369;
  49. }
  50. .dig-page .ignore{
  51. display: inline-block;
  52. width: 32px;
  53. height: 32px;
  54. color: #369;
  55. line-height: 32px;
  56. text-align: center;
  57. margin-right:6px;
  58. }
  59. .dig-page .active-page{
  60. display: inline-block;
  61. min-width:34px;
  62. height: 34px;
  63. font-weight:;
  64. color: #333;
  65. line-height: 34px;
  66. text-align: center;
  67. margin-right:6px;
  68. font-family: "Arial","Microsoft YaHei","黑体","宋体",sans-serif;
  69. }

分页标签 Css

页面登陆验证(装饰器方式实现)

1.普通登陆验证

  1. LOGIN_URL = '/login'
  2.  
  3. def auth_login_redirect(func):
  4.  
  5. def inner(self, *args, **kwargs):
  6. if not self.session['is_login']:
  7. self.redirect(config.LOGIN_URL)
  8. return
  9. func(self, *args, **kwargs)
  10. return inner

2.ajax提交数据的登陆验证

  1. def auth_login_json(func):
  2.  
  3. def inner(self, *args, **kwargs):
  4. if not self.session['is_login']:
  5. rep = BaseResponse()
  6. rep.summary = "auth failed"
  7. self.write(json.dumps(rep.__dict__))
  8. return
  9. func(self, *args, **kwargs)
  10. return inner
文件上传

为了美观 , 文件上传标签一半由两部分组成 file标签和 button标签

  1. <div id="main" class="up-block">
  2. <input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/>
  3. <input type="button" name="action" value="Upload" class="a-upload"/>
  4. <div>

file标签会设置 透明度 和 定位在button上 , 绑定onchange事件

  1. .up-block{
  2. position: relative;
  3. }
  4. .file-path{
  5. position: absolute;
  6. width: ..px;
  7. height: ..px;
  8. font-size: ..px;
  9. opacity: 0;
  10. z-index: 10;
  11. }

图片上传后,展示在上传页面:

方法一:利用iframe实现

  1. <div id="main" class="up-block">
  2. <input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/>
  3. <input type="button" name="action" value="Upload" class="a-upload"/>
  4. <div>
  5. <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
  6.  
  7. function imagechange(){
  8. imgupload();
  9. }
  10. // iframe图片上传
  11. function imgupload(){
  12. document.getElementById('my_iframe').onload = Testt;
  13. document.getElementById('my_form').target = 'my_iframe';
  14. document.getElementById('my_form').submit();
  15. }
  16. function Testt(ths) {
  17. var r = $("#my_iframe").contents().find("body").text();
  18. ret = JSON.parse(r); // 获得后台返回的数据...
  19. }

抽屉网实现,完整代码:

  1. //图片上传按钮:
  2. $(".a-upload").on("change","input[type='file']",function(){
  3. imgupload()
  4. });
  5. // iframe图片上传
  6. function imgupload(){
  7. document.getElementById('my_iframe').onload = Testt;
  8. document.getElementById('my_form').target = 'my_iframe';
  9. document.getElementById('my_form').submit();
  10. }
  11. function Testt(ths) {
  12. var r = $("#my_iframe").contents().find("body").text();
  13. ret = JSON.parse(r);
  14. if( ret.status ){
  15. var img_path = ret['message']['file_path'];
  16. console.log(img_path);
  17. var pre = 'http://localhost:8888/';
  18. $('.img-alt').addClass('hide');
  19. $('.upload-btn').addClass('upload-btn-show');
  20. $('.upload-show').removeClass('hide').attr('src',pre+img_path);
  21. $(".upload-error").text('');
  22. }else{
  23. $.each(ret['message'],function (k,v) {
  24. $(".upload-error").text(v).show();
  25. console.log(v);
  26. //清除信息:
  27. $('#uploadimage').val('');
  28. $(".img-alt").text("支持jpg、jpeg、gif、png格式,且不超过5MB");
  29. $(".upload-error").text("您上传的图片格式不合法,请重新上传").show();
  30. return false;
  31. });
  32. }
  33. }

jQ 代码

  1. <div class="img-upload-block">
  2. <img src="" alt="" class="upload-show hide"/>
  3. <div class="upload-btn">
  4. <a href="javascript:;" class="a-upload">上传
  5. <input type="file" name="uploadimage" id="uploadimage">
  6. </a>
  7. </div>
  8. <span class="img-alt">支持jpg、jpeg、gif、png格式,且不超过5MB</span>
  9. </div>

Html 代码

  1. /*上传文件按钮*/
  2. .a-upload {
  3. position: relative;
  4. display: inline-block;
  5. background: url('http://dig.chouti.com/images/bottom.png?v=2.8')no-repeat center center;
  6. background-position: 0 0px;
  7. border-radius: 4px;
  8. padding: 4px 16px;
  9. overflow: hidden;
  10. color: white;
  11. font-weight:;
  12. text-decoration: none;
  13. text-indent:;
  14. line-height: 20px;
  15. margin: 10px 0 0 6px;
  16. }
  17. .a-upload input {
  18. position: absolute;
  19. right:;
  20. top:;
  21. opacity:;
  22. }
  23. .a-upload:hover {
  24. color: white;
  25. background-position: 0 -33px;
  26. text-decoration: none;
  27. }
  28. .pic-content .img-upload-block .img-alt{
  29. color: #8ca1c1;
  30. padding-left: 6px;
  31. vertical-align: -12px;
  32. margin-left: 4px;
  33. font-size: 12px;
  34. font-family: "\5b8b\4f53";
  35. }

Css 代码

方法二:ajax实现

通过Ajax的FormData对象来实现。详情参考ajax篇

  1. <input type="file" name="uploadimage" id="uploadimage">
  2.  
  3. var fileObj = $("#uploadimage")[0].files[0];
  4. var form = new FormData();
  5. form.append("uploadimage", fileObj);
  6. $.ajax({
  7. url:'/uploadimg',
  8. type:'POST',
  9. data:form,
  10. processData: false, // tell jQuery not to process the data
  11. contentType: false,
  12. success:function (data, statusText, xmlHttpRequest) {
  13. obj = JSON.parse(data);
  14. }
  15. })

  

  

【Python之路】特别篇--抽屉新热榜的更多相关文章

  1. Python之路【第二十篇】:python项目之旧版抽屉新热榜

    旧版抽屉新热榜 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  2. 【IOS】模仿"抽屉新热榜"动态启动页YFSplashScreen

    IOS最好要设置系统默认启动页面,不然进入应用就会突然闪现黑色画面 下图是我们要实现的效果: 总体思路:设置一个系统默认启动页面,在进入didFinishLaunchingWithOptions时, ...

  3. 利用scrapy获取抽屉新热榜的标题和内容以及新闻地址保存到本地

    1.安装scrapy pip3 install scrapy 2.打开terminal,cd 到想要创建程序的目录下 3.创建一个scrapy项目 在终端输入:scrapy startproject ...

  4. python之路入门篇

    一. Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,Guido开始写能够解释Python语言语法的解释器.Python这个名字,来 ...

  5. python之路——基础篇(2)模块

    模块:os.sys.time.logging.json/pickle.hashlib.random.re 模块分为三种: 自定义模块 第三方模块 内置模块 自定义模块 1.定义模块 将一系列功能函数或 ...

  6. python之路基础篇

    基础篇 1.Python基础之初识python 2.Python数据类型之字符串 3.Python数据类型之列表 4.Python数据类型之元祖 5.Python数据类型之字典 6.Python Se ...

  7. python之路第二篇(基础篇)

    入门知识: 一.关于作用域: 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. if 10 == 10: name = 'allen' print name 以下结论对吗? ...

  8. Python之路(第四十一篇)线程概念、线程背景、线程特点、threading模块、开启线程的方式

    一.线程 ​ 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是 ...

  9. python之路第一篇

    一.python环境的搭建 1.window下环境的搭建 (1).在 https://www.python.org/downloads/ 下载自己系统所需要的python版本 (2).安装python ...

随机推荐

  1. SQLite进阶-15.触发器

    目录 触发器(Trigger) 触发器(Trigger)的要点: 触发器应用 查看触发器 删除触发器 触发器(Trigger) 触发器(Trigger)是数据库的回调函数,它会在指定的数据库事件发生时 ...

  2. (六)Spring 中的 JdbcTemplate

    目录 概念 配置数据库 创建 JdbcTemplate 对象 增删改查代码 概念 JdbcTemplate : 是 Spring 中对持久层(JDBC 技术)一个封装 : 使用起来和 Dbutuis ...

  3. docker-get拉取镜像

    docker-get Let you get all docker images without having network problem. Install curl -kLO https://s ...

  4. hdu 6069 Counting divisors 公式+区间筛

    比赛的时候把公式扣出来了,,但是没有想到用筛法算公因子,,默默学习一下.. 题解:设n=p1^(c1)p2^{c2}...pm^{cm},n=p​1^​c​1*​​​​p​2​^c​2​​​​...p ...

  5. 使用X.509数字证书加密解密实务(二)-- 使用RSA证书加密敏感数据

    一.  使用RSA证书加.解密敏感数据 X.509证书标准支持三种不对称加密算法:RSA, DSA, Diffie-Hellman algorithms.最常用的是RSA算法.所以本文就以前面章节使用 ...

  6. VBA开发项目分享-1

    这个项目的目的是使用VBA制作一个股票筛选器,股票的指标数据存放在多个工作表,输入多个指标的查询条件,可以从相应的工作表里查询出符合条件的股票数据并返回.项目涉及的VBA知识结构有字典.数组.OLED ...

  7. NetScaler Logs Collection Guide

    NetScaler Logs Collection Guide 来源  https://support.citrix.com/article/CTX227560 Article | Authentic ...

  8. react route使用HashRouter和BrowserRouter的区别-Content Security Policy img-src 404(Not found)

    踩坑经历 昨天看了篇关于react-route的文章,说BrowserRouter比HashRouter好一些,react也是推荐使用BrowserRouter,毕竟自己在前端方面来说,就是个小白,别 ...

  9. adb实操

    一.命令 adb connect IP:5555 adb disconnect IP:5555 adb remount adb install 安装包的绝对路径 二.获取logcat信息 1.制作文件 ...

  10. Java中接口是否可以继承多个接口?

    可以. 接口是常量值和方法定义的集合.接口是一种特殊的抽象类. java类是单继承的.classB Extends classA java接口可以多继承.Interface3 Extends Inte ...