註冊功能:javascript
流程: 填寫用戶名,郵箱,獲取郵箱驗證碼,填入密碼 單擊<下一步>按鈕,完成註冊!html
1.獲取郵箱驗證碼(具體步驟分析):前端
1.利用ajax 日後臺傳入郵箱,java
2.後臺表單驗證,郵箱填寫是否正確, python
=> 成功,則繼續3,git
=> 錯誤,寫入錯誤信息,頁面輸出錯誤提示!web
3.查詢sendcode 數據庫表,查詢該郵箱是不是第一次發送驗證碼ajax
=> 第一次發送: 數據表中插入記錄 (郵箱,發送次數1,郵箱驗證碼,狀態1,有效時間:當前時間-1小時)正則表達式
=> 不然: 從數據表中查詢出該郵箱驗證碼發送次數,redis
=> 若是 >5 則,再判斷上次發送時間+1小時 是否大於當前時間!
=> 大於當前時間,則發送驗證碼,修改數據表記錄 (發送次數+1,郵箱驗證碼,有效時間)
=> 小於當前時間,則爲屢次惡意發送,不操做,寫入錯誤信息,須要等待1小時,
=> 若是<5 則直接發送驗證碼,修改數據表記錄 (發送次數+1,郵箱驗證碼,有效時間)
4.json信息返回。
import json import datetime from core.request_handle import BaseHandler # 公共方法 from backend import commons,message # 數據庫查詢 from models.sendcode import Insert,QueryCounts,Update from models.userinfo import InsertUser # 表單驗證 from forms.account import emailForm,registerForm class SendEmailCodeHandler(BaseHandler): def post(self): obj = emailForm() val, success_info, error_info = obj.check_value(self) if not error_info: email = [success_info['email']] now = datetime.datetime.now() que = QueryCounts() ret = que.checkstime(email) if ret: if ret['stime'] < 5: UpdateCode(email) else: if (ret['ctime'] + datetime.timedelta(hours=1)) > now : error_info['stime'] = '驗證碼發送次數過多,請1小時後再發送' else: UpdateCode(email) else: InsertCode(email) data = {'success_info': success_info, 'error_info': error_info} self.write(json.dumps(data)) # 第一次發送code 數據表添加 def InsertCode(email): code = commons.random_code() # 生成4位隨機驗證碼(字母+數字) message.send_email(email, code) ins = Insert() ins.insertcode(email, code) print(email, code) # 屢次發送, 數據表修改 def UpdateCode(email): code = commons.random_code() message.send_email(email, code) update = Update() update.updatestime(email, code) print(email, code)
import random import string def random_code(): code = ''.join(random.sample(string.ascii_letters + string.digits, 4)) return code
//註冊:生成郵箱驗證碼: $('#email-code').on('click',function () { var email = $('#re-email').val(); $.ajax({ url:'/email-code', type: 'POST', dataType: 'json', data:{'email':email}, success: function(obj){ if( isEmptyObject(obj['error_info']) ){ $('.register-error').text(''); $('.ac-code').addClass('hide'); $('.ac-time').removeClass('hide'); timechange(); } $.each(obj['error_info'],function (k,v) { $('.register-error').text(v); return false; }); } }) });
2.驗證碼發送過程: 頁面倒計時
原理:設計計時器,每1秒修改1次text值, 時間小於0時,取消計時器
//郵箱驗證碼倒計時: function timechange() { var time = 60; var interval = setInterval(function () { if(time <= 0 ){ $('.ac-time').addClass('hide'); $('.ac-code').removeClass('hide'); clearInterval(interval); } time = time - 1; var temp = "已發送("+time+"s)"; $('.ac-time').text(temp) },1000) }
3.賬號註冊
1.JQ獲取前臺輸入表單內容
2.ajax傳輸給後臺,創建表單驗證
=> 正確,繼續3
=> 錯誤,返回錯誤信息,頁面顯示錯誤信息
3.數據庫中查詢用戶郵箱是否存在
=> 不存在,插入數據庫,繼續4
=> 存在,返回錯誤信息,頁面顯示
4.註冊帳號完成,寫入session,註冊完成頁面刷新實現自動登陸
//註冊賬號 $('#register-next').on('click',function () { registerUser(); clearinput(); }); function registerUser(){ var username= $('#re-username').val(); var email = $('#re-email').val(); var code = $('#re-code').val(); var password = $('#re-password').val(); $.ajax({ url:'/register', type: 'POST', dataType: 'json', data:{'username':username,'email':email,'code':code,'password':password}, success: function(obj){ if( isEmptyObject(obj['error_info']) ){ $('.register-error').text(''); //跳轉(隱藏輸入頁面): $('.login-block').addClass('hide'); $('.register-info').removeClass('hide'); } $.each(obj['error_info'],function (k,v) { $('.register-error').text(v); return false; }); } }) }
class RegisterUserHandler(BaseHandler): def post(self): obj = registerForm() val, success_info, error_info = obj.check_value(self) print(val, success_info, error_info) if not error_info: ins = InsertUser() ins.inser(success_info['username'],success_info['password'],success_info['email']) self.session['is_login'] = 1 self.session['username'] = success_info['username'] data = {'success_info': success_info, 'error_info': error_info} self.write(json.dumps(data))
表單錯誤時,頁面信息提示(效果圖):
登陸功能:
原理:與註冊功能相似
1.圖片驗證碼:點擊驗證碼,自動更改
驗證碼爲後臺生成的圖片,每點擊一次,src地址後面 多加?實現切換
<div class="inp-block"> <input type="text" name="phoneregister" placeholder="請輸入驗證碼" id="u-code" class="phonenum" autocomplete="off"/> <img src="/check_code" onclick='ChangeCode();' id='imgCode' class="codeimg"> </div>
//註冊頁面驗證碼切換 function ChangeCode() { var code = document.getElementById('imgCode'); code.src += '?'; }
//登陸,肯定按鈕:(郵箱形式) $('.login-btn').on('click',function () { var username = $('#username').val(); var password = $('#password').val(); var code = $('#u-code').val(); var remember = 0; if($('#phone-remember').is(':checked')){ remember = 1; } $.ajax({ url:'/userlogin', type: 'POST', dataType: 'text', data:{'username':username,'password':password,'code':code,'remember':remember}, success: function(data, statusText, xmlHttpRequest){ obj = JSON.parse(data); if( isEmptyObject(obj['error_info']) ){ $('.login-error').text(''); window.location.reload(); } $.each(obj['error_info'],function (k,v) { $('.login-error').text(v); return false; }); } }) });
import io from backend.utils import check_code from core.request_handle import BaseHandler class CheckcodeHandler(BaseHandler): def get(self, *args, **kwargs): mstream = io.BytesIO() img, code = check_code.create_validate_code() self.session['code'] = code print(self.session['code']) img.save(mstream, "GIF") self.write(mstream.getvalue())
import tornado.ioloop import tornado.web import json import os import sys from core.request_handle import BaseHandler from models.userinfo import QueryUser from forms.account import LoginForm class LoginUserHandler(BaseHandler): def post(self, *args, **kwargs): obj = LoginForm() val, success_info, error_info = obj.check_value(self) print(val, success_info, error_info) if not error_info: if self.session['code'].upper() == success_info['code'].upper(): query = QueryUser() ret = query.checkusername(success_info['username'],success_info['password']) if ret: self.session['is_login'] = 1 self.session['username'] = ret['username'] self.session['user_id'] = ret['user_id'] else: error_info = { 'username' : '用戶名或密碼錯誤'} else: error_info = {'code': '驗證碼錯誤'} data = { 'success_info': success_info , 'error_info': error_info } self.write(json.dumps(data))
#!/usr/bin/env python #coding:utf-8 import random from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小寫字母,去除可能干擾的i,l,o,z _upper_cases = _letter_cases.upper() # 大寫字母 _numbers = ''.join(map(str, range(3, 10))) # 數字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="Monaco.ttf", length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance = 2): ''' @todo: 生成驗證碼圖片 @param size: 圖片的大小,格式(寬,高),默認爲(120, 30) @param chars: 容許的字符集合,格式字符串 @param img_type: 圖片保存的格式,默認爲GIF,可選的爲GIF,JPEG,TIFF,PNG @param mode: 圖片模式,默認爲RGB @param bg_color: 背景顏色,默認爲白色 @param fg_color: 前景色,驗證碼字符顏色,默認爲藍色#0000FF @param font_size: 驗證碼字體大小 @param font_type: 驗證碼字體,默認爲 ae_AlArabiya.ttf @param length: 驗證碼字符個數 @param draw_lines: 是否劃干擾線 @param n_lines: 干擾線的條數範圍,格式元組,默認爲(1, 2),只有draw_lines爲True時有效 @param draw_points: 是否畫干擾點 @param point_chance: 干擾點出現的機率,大小範圍[0, 100] @return: [0]: PIL Image實例 @return: [1]: 驗證碼圖片中的字符串 ''' width, height = size # 寬, 高 img = Image.new(mode, size, bg_color) # 建立圖形 draw = ImageDraw.Draw(img) # 建立畫筆 def get_chars(): '''生成給定長度的字符串,返回列表格式''' return random.sample(chars, length) def create_lines(): '''繪製干擾線''' line_num = random.randint(*n_line) # 干擾線條數 for i in range(line_num): # 起始點 begin = (random.randint(0, size[0]), random.randint(0, size[1])) #結束點 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): '''繪製干擾點''' chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width): for h in range(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): '''繪製驗證碼字符''' c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每一個字符先後以空格隔開 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 圖形扭曲參數 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 建立扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 濾鏡,邊界增強(閾值更大) return img, strs
設計思想: 不管表單經過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 定義錯誤信息輸出內容
class LoginForm(BaseForm): def __init__(self): self.username = UsernameField(required=True,error_dict={'required':'用戶名不能爲空' , 'valid':'用戶名錯誤'}) self.password = PasswordField(required=True,error_dict={'required':'密碼不能爲空' , 'valid':'用戶名或密碼錯誤'}) self.code = CodeField(required=True,error_dict={'required':'驗證碼不能爲空' , 'valid':'驗證碼錯誤'}) self.remember = CheckBoxField(required=False,error_dict={'valid':'格式錯誤'}) super(LoginForm, self).__init__()
2. 繼承BaseForm類,該類初始化了,當前Form表單共用的最後返回數據和錯誤信息的字典對象
class BaseForm(): def __init__(self): self._value_dict = {} self._error_dict = {} self._valid_status = True def check_value(self): ....
check_value() 執行獲取前端輸入的數據,self.get_argument(xxxx) 而且根據每一個input標籤訂義的規則去驗證數據的正確性 (上文的UsernameField,PasswordField..等)
經過self.__dict__循環獲取LoginForm的成員對象,調用Field的validate()方法,驗證Form表單中每個的值。驗證正確,信息存儲在self._value_dict 中, 錯誤信息存儲在self._error_dict 中
def check_value(self,handler): for key,regular in self.__dict__.items(): input_value = handler.get_argument(key,None) regular.validate(key,input_value) if regular.is_valid: self._value_dict[key] = regular.value else: self._error_dict[key] = regular.error
class BaseForm(): def __init__(self): self._value_dict = {} self._error_dict = {} self._valid_status = True def check_value(self,handler): for key,regular in self.__dict__.items(): if key.startswith('_'): continue if type(regular) == fields.CheckBoxField: input_value = handler.get_arguments(key) # checkbox取值 elif type(regular) == fields.FileField: file_list = handler.request.files.get(key,[]) # 文件對象 input_value = [] for item in file_list: input_value.append(item['filename']) else: input_value = handler.get_argument(key,None) regular.validate(key,input_value) if regular.is_valid: self._value_dict[key] = regular.value else: self._error_dict[key] = regular.error self._valid_status = False return self._valid_status
3. Field 自定義的規則類
爲前端input標籤,定義不一樣的驗證規則(正則表達式),驗證用戶輸入的數據
class PasswordField(Field): REGULAR = "[0-9 | A-Z | a-z]{6,16}" def __init__(self,required=True,error_dict=None): self.error_dict = {} #錯誤信息 if error_dict: self.error_dict.update(error_dict) #用戶自定義的錯誤信息 self.required = required super(PasswordField, self).__init__()
繼承父類Field,初始化存儲信息的成員
class Field: def __init__(self): self.is_valid = False # 驗證規則是否經過,默認False self.name = None self.value = None # 獲取的前端input值 self.error = None def validate(self, name, input_value): ...
執行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)
class Field: def __init__(self): self.is_valid = False self.name = None self.value = None self.error = None def validate(self, name, input_value): self.name = name if not self.required: # 能夠爲空 self.value = input_value self.is_valid = True else: if not input_value: if self.error_dict.get('required', None): self.error = self.error_dict['required'] else: self.error = '%s is requires ' % (name) else: val = re.match(self.REGULAR, input_value) if not val: if self.error_dict.get('valid', None): self.error = self.error_dict['valid'] else: self.error = '%s is valid ' % (name) else: self.value = input_value self.is_valid = True
自定義的Field 供參考:
class CheckBoxField(Field): REGULAR = "^\d+$" def __init__(self,required=True,error_dict=None): self.error_dict = {} if error_dict: self.error_dict.update(error_dict) self.required = required super(CheckBoxField, self).__init__() def validate(self,name,input_value): if not self.required: self.value = input_value self.is_valid = True else: if not input_value: if self.error_dict.get('required',None): self.error = self.error_dict['required'] else: self.error = '%s is requires '%(name) else: if isinstance(name, list): self.is_valid = True self.value = input_value else: if self.error_dict.get('valid', None): self.error = self.error_dict['valid'] else: self.error = "%s is invalid" % name
class FileField(Field): REGULAR = "^(\w+\.jpg)|(\w+\.jpeg)|(\w+\.gif)|(\w+\.png)$" def __init__(self, required=True, error_dict=None): self.error_dict = {} if error_dict: self.error_dict.update(error_dict) self.required = required self.value = [] self.success_file_name_list = [] super(FileField, self).__init__() def validate(self, name, all_file_name_list): self.name = name if not self.required: self.value = all_file_name_list else: if not all_file_name_list: self.is_valid = False if self.error_dict.get('required', None): self.error = self.error_dict['required'] else: self.error = '%s is requires ' % (name) else: for file_name in all_file_name_list: if not file_name or not file_name.strip(): if self.error_dict.get('required', None): self.error = self.error_dict['required'] else: self.error = "%s is required" % name self.is_valid = False break else: val = re.match(FileField.REGULAR, file_name) if not val: self.is_valid = False if self.error_dict.get('valid', None): self.error = self.error_dict['valid'] else: self.error = '%s is valid ' % (name) break else: self.value.append(file_name)
class EmailField(Field): REGULAR = "^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$" def __init__(self, required=True, error_dict=None): self.error_dict = {} # 錯誤信息 if error_dict: self.error_dict.update(error_dict) # 用戶自定錯誤信息 self.required = required super(EmailField, self).__init__()
設計思想: 利用Cookie 自定義一個Session來存儲每一個用戶信息
實現思路: 1. Session信息設計成一個大字典,key對應用戶惟一識別加密串,value對應空字典{}存儲用戶信息,存放在服務端上。
2. 爲用戶請求生成一個惟一加密串,寫入到Cookie信息中,Session_id = 加密串,用於區分每個用戶。
3. 當用戶請求到來時,獲取該請求的Cookie信息,判斷是否存在Session_id(用戶惟一識別加密串)?
=> 若是存在Session_id 而且在 Session大字典中找到相同的 key,記錄Session_id
=> 其餘狀況下一概,生成加密串,寫入到Cookie中,同時寫入到 Session大字典中
class CacheSession(): session_id = "__balabala__" # Cookie中爲Session存儲的名字 session_container = {} # Session大字典 def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie(CacheSession.session_id,None) if client_random_str and client_random_str in CacheSession.session_container: self.random_str = client_random_str else: self.random_str = self.__container__random__str() CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): # 生成加密串 hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str
利用方法:1. 當用戶請求到達每一個Handler時,咱們都須要先實例化一個CacheSession(),
2. 此時咱們能夠定義一個父類BaseHandler,initialize() 方法中寫入要執行代碼,
import tornado.web form session import SessionFactory class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = SessionFactory.get_session_obj(self)
能夠看到,咱們在session.py中定義了一個新的類SessionFactory,用來選擇合適的方法,初始化Session,
該類經過讀取配置文件config中的SESSION_TYPE選擇適合的Session類進行初始化,而且返回一個Session對象,該對象最終存儲在 self.session中。
class SessionFactory(): @staticmethod def get_session_obj(handler): if config.SESSION_TYPE == 'cache': obj = CacheSession(handler) elif config.SESSION_TYPE == 'memcached': obj = MemcachedSession(handler) elif config.SESSION_TYPE == 'redis': obj = RedisSession(handler) return obj
Handler 中使用Session
class LoginUserHandler(BaseHandler): def post(self, *args, **kwargs): self.session['is_login'] = 1 self.write('ok')
1.緩存Session
import time import hashlib import config import memcache import json import redis class CacheSession(): session_id = "__balabala__" session_container = {} def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie(CacheSession.session_id,None) if client_random_str and client_random_str in CacheSession.session_container: self.random_str = client_random_str else: self.random_str = self.__container__random__str() CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self,key,value): CacheSession.session_container[self.random_str][key] = value def __getitem__(self,key): result = CacheSession.session_container[self.random_str].get(key,None) return result def __delitem__(self,key): if key in CacheSession.session_container[self.random_str]: del CacheSession.session_container[self.random_str][key]
2.memcache session
conn = memcache.Client(['127.0.0.1:11210'], debug=True) class MemcachedSession(): session_id = "__balabala__" def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None) if client_random_str and conn.get(client_random_str): self.random_str = client_random_str else: self.random_str = self.__container__random__str() conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES) conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self,key,value): ret = conn.get(self.random_str) ret_dict = json.loads(ret) ret_dict[key] = value conn.set(self.random_str,json.dumps(ret_dict),config.SESSION_EXPIRES) def __getitem__(self,key): ret = conn.get(self.random_str) ret_dict = json.loads(ret) result = ret_dict.get(key,None) return result def __delitem__(self, key): ret = conn.get(self.random_str) ret_dict = json.loads(ret) del ret_dict[key] conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)
3.radis session
pool = redis.ConnectionPool(host='127.0.0.1', port=6379) r = redis.Redis(connection_pool=pool) class RedisSession(): session_id = "__balabala__" def __init__(self, handler): self.handler = handler client_random_str = self.handler.get_cookie(RedisSession.session_id, None) if client_random_str and r.exists(client_random_str): self.random_str = client_random_str else: self.random_str = self.__container__random__str() r.hset(self.random_str, None, None) r.expire(self.random_str,config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self, key, value): if type(value) == dict: r.hset(self.random_str, key, json.dumps(value)) else: r.hset(self.random_str, key, value) def __getitem__(self, key): ret = r.hget(self.random_str,key) if ret : ret_str = str(ret,encoding='utf-8') try: result = json.loads(ret_str) except: result = ret_str return result else: return ret def __delitem__(self, key): 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全部的完整代碼:
#!/usr/bin/env python # -*-coding:utf-8 -*- import time import hashlib import config import memcache import json import redis class CacheSession(): session_id = "__balabala__" session_container = {} def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie(CacheSession.session_id,None) if client_random_str and client_random_str in CacheSession.session_container: self.random_str = client_random_str else: self.random_str = self.__container__random__str() CacheSession.session_container[self.random_str] = {} expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self,key,value): CacheSession.session_container[self.random_str][key] = value def __getitem__(self,key): result = CacheSession.session_container[self.random_str].get(key,None) return result def __delitem__(self,key): if key in CacheSession.session_container[self.random_str]: del CacheSession.session_container[self.random_str][key] conn = memcache.Client(['127.0.0.1:11210'], debug=True) class MemcachedSession(): session_id = "__balabala__" def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie(MemcachedSession.session_id,None) if client_random_str and conn.get(client_random_str): self.random_str = client_random_str else: self.random_str = self.__container__random__str() conn.set(self.random_str,json.dumps({}),config.SESSION_EXPIRES) conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self,key,value): ret = conn.get(self.random_str) ret_dict = json.loads(ret) ret_dict[key] = value conn.set(self.random_str,json.dumps(ret_dict),config.SESSION_EXPIRES) def __getitem__(self,key): ret = conn.get(self.random_str) ret_dict = json.loads(ret) result = ret_dict.get(key,None) return result def __delitem__(self, key): ret = conn.get(self.random_str) ret_dict = json.loads(ret) del ret_dict[key] conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES) pool = redis.ConnectionPool(host='127.0.0.1', port=6379) r = redis.Redis(connection_pool=pool) class RedisSession(): session_id = "__balabala__" def __init__(self, handler): self.handler = handler client_random_str = self.handler.get_cookie(RedisSession.session_id, None) if client_random_str and r.exists(client_random_str): self.random_str = client_random_str else: self.random_str = self.__container__random__str() r.hset(self.random_str, None, None) r.expire(self.random_str,config.SESSION_EXPIRES) expires_time = time.time() + config.SESSION_EXPIRES self.handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time) # 方便後續定義過時時間! def __container__random__str(self): hash = hashlib.md5() hash.update(bytes(str(time.time()), encoding='utf-8')) random_str = hash.hexdigest() return random_str def __setitem__(self, key, value): if type(value) == dict: r.hset(self.random_str, key, json.dumps(value)) else: r.hset(self.random_str, key, value) def __getitem__(self, key): ret = r.hget(self.random_str,key) if ret : ret_str = str(ret,encoding='utf-8') try: result = json.loads(ret_str) except: result = ret_str return result else: return ret def __delitem__(self, key): r.hdel(self.random_str, key) class SessionFactory(): @staticmethod def get_session_obj(handler): if config.SESSION_TYPE == 'cache': obj = CacheSession(handler) elif config.SESSION_TYPE == 'memcached': obj = MemcachedSession(handler) elif config.SESSION_TYPE == 'redis': obj = RedisSession(handler) return obj
#!/usr/bin/env python # -*-coding:utf-8 -*- import tornado.ioloop import tornado.web from backend.session.session import SessionFactory class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = SessionFactory.get_session_obj(self)
#!/usr/bin/env python # -*-coding:utf-8 -*- # Session類型:cache/redis/memcached SESSION_TYPE = "cache" # Session超時時間(秒) SESSION_EXPIRES = 180
設計思路:與Tornado篇分頁同理
前端:
<div class="pagination"> {% raw str_page%} #展現原生html </div>
後臺Url配置:
(r"/index/(?P<page>\d*)", IndexHandler),
後臺Handle:
class IndexHandler(BaseHandler): def get(self, page): query = QueryContent() page_obj = pager.Pagiantion(page, all_item=query.queryCounts(), per_num=5) #page 當前頁碼 , all_item 總數據條數, per_num 每頁顯示條數 str_page = page_obj.page_str('/index/') self.render("index.html", str_page=str_page )
Pagiantion 分頁類:
class Pagiantion: def __init__(self,current_page,all_counts,per_num): all_page, c = divmod(all_counts, per_num) if c>0: all_page += 1 self.all_page = all_page try: current_page = int(current_page) except: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page def page_str(self,base_url): if self.all_page < 10: s = 1 t = self.all_page else: if self.current_page < 7: s = 1 t = 10 else: if self.all_page - self.current_page >= 4 : s = self.current_page - 3 t = self.current_page + 4 else: s = self.all_page - 6 t = self.all_page list_page = [] # 上一頁 if self.current_page != 1: pre_page = "<a href='%s%s' class='pageedg'>上一頁</a>" % (base_url, self.current_page - 1,) list_page.append(pre_page) # 頁碼生成 # 生成 1 , ... , if( self.current_page >= 7 ): temp = "<a class='pageNum' href='%s%s'>%s</a>" % (base_url, 1, 1) list_page.append(temp) temp = "<span class='ignore' >...</span>" list_page.append(temp) for i in range(s, t + 1): if i == self.current_page: temp = "<span class='active-page' href='%s%s'>%s</span>" % (base_url,i, i) else: temp = "<a href='%s%s' class='pageNum'>%s</a>" % (base_url , i, i) list_page.append(temp) # 下一頁 if self.current_page < self.all_page: next_page = "<a href='%s%s' class='pageedg'>下一頁</a>" % (base_url, self.current_page + 1,) list_page.append(next_page) # 數據拼接 返回 str_page = ''.join(list_page) # 列表鏈接成爲字符串 return str_page
/*分頁*/ .dig-page-block{ width: 640px; height: 40px; /*border: 1px solid red;*/ float: left; margin:20px 0 60px 0; } .dig-page { width:630px; height: 38px; } .dig-page a.pageNum{ display: inline-block; min-width:34px; height: 34px; color: #369; line-height: 34px; text-align: center; border: 1px solid #e1e1e1; border-radius: 5px 5px; margin-right:6px; text-decoration: none; font-family: "Arial","Microsoft YaHei","黑體","宋體",sans-serif; } .dig-page a.pageNum:hover{ color: white;color: #fff; background-color: #369; border: 1px solid #369; } /*頁碼中文字體*/ .dig-page a.pageedg{ display: inline-block; width: 77px; height: 34px; color: #369; line-height: 34px; text-align: center; border: 1px solid #e1e1e1; border-radius: 5px 5px; margin-right:6px; text-decoration: none; font-family: "Arial","Microsoft YaHei","黑體","宋體",sans-serif; } .dig-page a.pageedg:hover{ color: #fff; background-color: #369; border: 1px solid #369; } .dig-page .ignore{ display: inline-block; width: 32px; height: 32px; color: #369; line-height: 32px; text-align: center; margin-right:6px; } .dig-page .active-page{ display: inline-block; min-width:34px; height: 34px; font-weight: 700; color: #333; line-height: 34px; text-align: center; margin-right:6px; font-family: "Arial","Microsoft YaHei","黑體","宋體",sans-serif; }
1.普通登錄驗證
LOGIN_URL = '/login' def auth_login_redirect(func): def inner(self, *args, **kwargs): if not self.session['is_login']: self.redirect(config.LOGIN_URL) return func(self, *args, **kwargs) return inner
2.ajax提交數據的登錄驗證
def auth_login_json(func): def inner(self, *args, **kwargs): if not self.session['is_login']: rep = BaseResponse() rep.summary = "auth failed" self.write(json.dumps(rep.__dict__)) return func(self, *args, **kwargs) return inner
爲了美觀 , 文件上傳標籤一半由兩部分組成 file標籤和 button標籤
<div id="main" class="up-block"> <input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/> <input type="button" name="action" value="Upload" class="a-upload"/> <div>
file標籤會設置 透明度 和 定位在button上 , 綁定onchange事件
.up-block{ position: relative; } .file-path{ position: absolute; width: ..px; height: ..px; font-size: ..px; opacity: 0; z-index: 10; }
圖片上傳後,展現在上傳頁面:
方法一:利用iframe實現
<div id="main" class="up-block"> <input name="file" id="my_file" class="file-path" type="file" onchange="imagechange()"/> <input type="button" name="action" value="Upload" class="a-upload"/> <div> <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe> function imagechange(){ imgupload(); } // iframe圖片上傳 function imgupload(){ document.getElementById('my_iframe').onload = Testt; document.getElementById('my_form').target = 'my_iframe'; document.getElementById('my_form').submit(); } function Testt(ths) { var r = $("#my_iframe").contents().find("body").text(); ret = JSON.parse(r); // 得到後臺返回的數據... }
抽屜網實現,完整代碼:
//圖片上傳按鈕: $(".a-upload").on("change","input[type='file']",function(){ imgupload() }); // iframe圖片上傳 function imgupload(){ document.getElementById('my_iframe').onload = Testt; document.getElementById('my_form').target = 'my_iframe'; document.getElementById('my_form').submit(); } function Testt(ths) { var r = $("#my_iframe").contents().find("body").text(); ret = JSON.parse(r); if( ret.status ){ var img_path = ret['message']['file_path']; console.log(img_path); var pre = 'http://localhost:8888/'; $('.img-alt').addClass('hide'); $('.upload-btn').addClass('upload-btn-show'); $('.upload-show').removeClass('hide').attr('src',pre+img_path); $(".upload-error").text(''); }else{ $.each(ret['message'],function (k,v) { $(".upload-error").text(v).show(); console.log(v); //清除信息: $('#uploadimage').val(''); $(".img-alt").text("支持jpg、jpeg、gif、png格式,且不超過5MB"); $(".upload-error").text("您上傳的圖片格式不合法,請從新上傳").show(); return false; }); } }
<div class="img-upload-block"> <img src="" alt="" class="upload-show hide"/> <div class="upload-btn"> <a href="javascript:;" class="a-upload">上傳 <input type="file" name="uploadimage" id="uploadimage"> </a> </div> <span class="img-alt">支持jpg、jpeg、gif、png格式,且不超過5MB</span> </div>
/*上傳文件按鈕*/ .a-upload { position: relative; display: inline-block; background: url('http://dig.chouti.com/images/bottom.png?v=2.8')no-repeat center center; background-position: 0 0px; border-radius: 4px; padding: 4px 16px; overflow: hidden; color: white; font-weight: 700; text-decoration: none; text-indent: 0; line-height: 20px; margin: 10px 0 0 6px; } .a-upload input { position: absolute; right: 0; top: 0; opacity: 0; } .a-upload:hover { color: white; background-position: 0 -33px; text-decoration: none; } .pic-content .img-upload-block .img-alt{ color: #8ca1c1; padding-left: 6px; vertical-align: -12px; margin-left: 4px; font-size: 12px; font-family: "\5b8b\4f53"; }
方法二:ajax實現
經過Ajax的FormData對象來實現。詳情參考ajax篇
<input type="file" name="uploadimage" id="uploadimage"> var fileObj = $("#uploadimage")[0].files[0]; var form = new FormData(); form.append("uploadimage", fileObj); $.ajax({ url:'/uploadimg', type:'POST', data:form, processData: false, // tell jQuery not to process the data contentType: false, success:function (data, statusText, xmlHttpRequest) { obj = JSON.parse(data); } })