1. tornado框架的简单配置与应用
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.write("Hello World")
self.render("s1.html")
def post(self):
self.write("Hello World")
settings = {
"template_path": "template", # 模板路径的配置
"static_path": "static", # 静态文件路径
}
# 路由系统,或路由映射
application = tornado.web.Application([
(r"/index", MainHandler)
], **settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Tornado程序
基本步骤:
继承类
application
run
配置文件
模板路径
2. 使用tornado的模板语言
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
INPUTS_LIST = []
class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.write("Hello World")
# 1. 打开s1.html文件,读取内容(包含特殊语法)
# 2. numbers = [1, 2, 3, 4, 5] && 读取内容(包括特殊语法)
# 3. 得到新的字符串
# 4. 返回给用户
self.render("s1.html", names=INPUTS_LIST)
def post(self, *args, **kwargs):
name = self.get_argument("txt")
print(name)
INPUTS_LIST.append(name)
self.render("s1.html", names=INPUTS_LIST)
settings = {
"template_path": "template", # 模板路径的配置
"static_path": "static", # 静态文件路径
}
# 路由系统,或路由映射
application = tornado.web.Application([
(r"/index", MainHandler)
], **settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="static/commons.css" rel="stylesheet">
</head>
<body>
<h1>H提交内容:</h1>
<form method="post" action="/index">
<input tyoe="text" name="txt">
<input type="submit" value="提交">
</form>
<h1>展示内容:</h1>
<ul>
{% for item in names%}
<li>{{item}}</li>
{% end %}
</ul>
</body>
</html>
self.get_argument("txt") # 获取用户提交的数据
{% for item in names%}
<li>{{item}}</li>
{% end %} # 模板语言里通过for循环展示数据
{{}}
{% if %} {% end %}
自定义函数、类:uimethod uimodule
代码案例:
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
import uimethod as mt
import uimodule as md
INPUTS_LIST = []
class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.write("Hello World")
# 1. 打开s1.html文件,读取内容(包含特殊语法)
# 2. numbers = [1, 2, 3, 4, 5] && 读取内容(包括特殊语法)
# 3. 得到新的字符串
# 4. 返回给用户
self.render("s1.html", names=INPUTS_LIST)
def post(self, *args, **kwargs):
name = self.get_argument("txt", None)
if name:
INPUTS_LIST.append(name)
print(name)
self.render("s1.html", names=INPUTS_LIST)
settings = {
"template_path": "template", # 模板路径的配置
"static_path": "static", # 静态文件路径
"ui_methods": mt,
"ui_modules": md,
}
# 路由系统,或路由映射
application = tornado.web.Application([
(r"/index", MainHandler)
], **settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="static/commons.css" rel="stylesheet">
</head>
<body>
<h1>H提交内容:</h1>
<form method="post" action="/index">
<input tyoe="text" name="txt">
<input type="submit" value="提交">
</form>
<h1>展示内容:</h1>
<h2>{{ func("a") }}</h2>
<h3>{% module custom() %}</h3>
<ul>
{% for item in names%}
{% if item == "Steve" %}
<li style="color:red">{{item}}</li>
{% else %}
<li>{{item}}</li>
{% end %}
{% end %}
</ul>
</body>
</html>
# -*-coding:utf-8-*-
# uimethod
def func(self, arg):
print(arg)
return "Method"
# -*-coding:utf-8-*-
# uimodule
from tornado.web import UIModule
from tornado import escape
class custom(UIModule):
def render(self, *args, **kwargs):
return "Module"
4. 内置模板方法
Tornado在模板中默认提供了一些函数、字段、类以供模板使用:
escape:tornado.escape.xhtml_escape 的别名
xhtml_escape:tornado.escape.xhtml_escape 的别名
url_escape:tornado.escape.url_escape 的别名
json_encode:tornado.escape.json_encode 的别名
squeeze:tornado.escape.squeeze 的别名
linkify:tornado.escape.linkify 的别名
datetime:Python的datetime模块
handler:当前的RequestHandler对象
request:handler.request的别名
current_user:handler.current_user 的别名
locale:handler.locale 的别名
_: handler.locale.translate 的别名
static_url: handler.static_url 的别名
xsrf_form_html:handler.xsrf_form_html 的别名
Tornado默认提供的这些功能其本质上就是UIMethod和UIModule。
5. Tornado之cookie
代码案例:
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
INPUTS_LIST = []
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("index.html", )
class ManageHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
cookie = self.get_cookie("auth")
if cookie == "1":
self.render("/manage.html")
else:
self.redirect("/login")
class LoginHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("login.html", status_text="")
def post(self, *args, **kwargs):
username = self.get_argument("username", None)
pwd = self.get_argument("password", None)
if username == "Steve" and pwd == "123":
# 表示要设定cookie值
self.set_cookie("auth", "1", expires=10) # expires后面的参数为有效期限
self.redirect("/manage")
else:
self.render("login.html", status_text="登录失败")
class LogoutHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.set_cookie("auth", 0)
self.redirect("/login")
settings = {
"template_path": "views", # 模板路径的配置
}
# 路由系统,或路由映射
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/login", LoginHandler),
(r"/manage", ManageHandler),
(r"/logout", LogoutHandler)
], **settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="登录">
<span style="color:red">{{status_text}}</span>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/logout">退出</a>
<h1>您的数据</h1>
<h2>身高:170cm</h2>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>
cookie很容易被恶意的客户端伪造。假如想在cookie中保存当前登录用户的id之类的信息,你需要对cookie做签名以防止伪造。Tornado通过set_secure_cookie和get_secure_cookie方法直接支持了这种功能。要使用这些方法,需要在创建应用时提供一个密钥,名字为cookie_secret。可以把它作为一个关键词参数传入应用的设置中:
6. 原生Ajax
1)概述
Ajax主要就是使用XMLHttpRequest对象来完成请求的操作,该对象在主流浏览器中均存在。
2)XMLHttpRequest对象的主要方法
a. void open(String method, String url, Boolean async)
用于创建请求
参数:
method: 请求方式(字符串类型),如:POST、GET、DELETE
url:要请求的地址(字符串类型)
async:是否异步(布尔类型)
b. void send(String body)
用于发送请求
参数:
body:要发送的数据(字符串类型)
c. void setRequestHeader(String header, String value)
用于设置请求头
参数:
header: 请求头的key(字符串类型)
value:请求头的value(字符串类型)
d. String getAllResponseHeaders()
获取所有响应头
返回值:响应头数据(字符串类型)
e. String getResponseHeader(String header)
获取响应头中指定header的值
参数:
header:响应头的key(字符串类型)
返回值:
响应头中指定的header相应的值
f. void abort()
终止请求
3)XMLHttpRequest对象的主要属性
a. Number readyState 状态值(整数)
详情:
0:未初始化,尚未调用oepn()方法;
1:启动,调用了open()方法,为调用send()方法;
2:发送,已经调用了send()方法,未接收到响应;
3:接受,已经接受到部分相应数据;
4:完成,已经接受到全部相应数据;
b. Function onreadystatechange
当readyState的值改变时自动触发执行其对应的函数(回调函数)
c. String responseText
服务器返回的数据(字符串类型)
d. XmlDocument responseXML
服务器返回的数据(xml对象)
e. Number states
状态码(整数),如:200、404
f. String statesText
状态文本(字符串),如:OK、NotFound
3)发送Ajax请求:
xhr = new XMLHttpRequest()
xhr.onreadystatechange = func
xhr.open("GET", "url", true)
xhr.send()
4)案例代码
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
import time
INPUTS_LIST = []
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("index.html", )
class ManageHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
cookie = self.get_cookie("auth")
if cookie == "1":
self.render("/manage.html")
else:
self.redirect("/login")
class LoginHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("login.html", status_text="")
def post(self, *args, **kwargs):
dic = {"status": True, "message": ""}
user = self.get_argument("username", None)
pwd = self.get_argument("password", None)
if user == "Steve" and pwd == "123":
pass
else:
dic["status"] = False
dic["message"] = "用户名或密码错误"
import json
self.write(json.dumps(dic))
class LogoutHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.set_cookie("auth", "0", expires=time.time())
self.redirect("/login")
settings = {
"template_path": "views", # 模板路径的配置
"cookie_secret": "vjovuanefaelut",
"static_path": "statics"
}
# 路由系统,或路由映射
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/login", LoginHandler),
(r"/manage", ManageHandler),
(r"/logout", LogoutHandler)
], **settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="user" type="text" name="username">
<input id="pwd" type="password" name="password">
<button onclick="SubmitForm();">登录</button>
<script type="text/javascript">
xhr = null;
function SubmitForm(){
xhr = new XMLHttpRequest();
xhr.open("POST", "/login", true);
xhr.onreadystatechange = func;
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded; charset=UTF-8");
xhr.send("username=" + document.getElementById("user").value
+ ";password=" + document.getElementById("pwd").value);
}
function func(){
if(xhr.readyState == 4){
console.log(xhr.responseText);
var data = xhr.responseText;
var ret_dic = JSON.parse(data);
if(ret_dic.status){
}else{
alter(ret_dict.message);
}
}
}
</script>
</body>
</html>
5)利用jQuery使用ajax的代码案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="user" type="text" name="username">
<input id="pwd" type="password" name="password">
<button onclick="SubmitForm();">登录</button>
<script src="{{ static_url(jquery-3.2.1.js) }}">
username = $("#user").value();
password = $("#pwd").value();
function SubmitForm(){
$.post("/login", {"username": username, "password": password}, function(callback){
console.log(callback);
});
}
</script>
</body>
</html>
7. Tornado的路由系统
1)基本路由
# 路由映射,基本路由
application = tornado.web.Application([
(r"/index", home.IndexHandler)
], **settings)
2)基础正则路由
# 路由映射,基于正则的动态路由
application = tornado.web.Application([
(r"/index/(?P<num>\d*)(?P<nid>\d*)", home.IndexHandler)
], **settings)
Tornado自定义分页代码案例:
# -*-coding:utf-8-*-
import tornado.web
import tornado.ioloop
import home
settings = {
"template_path": "views", # 模板路径的配置
"static_path": "statics", # 静态文件
}
# 路由映射,基于正则的动态路由
application = tornado.web.Application([
(r"/index/(?P<page>\d*)", home.IndexHandler)
], **settings)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
# -*-coding:utf-8-*-
import tornado.web
LIST_INFO = [
{"username":"Steve", "email":"[email protected]"},
]
for i in range(99):
temp = {"username": "John" + str(i), "email": str(i) + "[email protected]"}
LIST_INFO.append(temp)
# 每页显示5条数据,page是当前页
# 第一页:0:5
# LIST_INFO[0:5]
# 第二页:5:10
# LIST_INFO[5:10]
# start: (page - 1) * 5
# end: page * 5
class PageDivision:
def __init__(self, current_page, all_item):
try:
current_page = int(current_page)
except:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
all_pager, c = divmod(all_item, 5)
if c > 0:
all_pager += 1
self.all_pager = all_pager
@property
def start(self):
return (self.current_page - 1) * 5
@property
def end(self):
return self.current_page * 5
def page_str(self, base_url):
list_page = []
# former, later; former代表当前页的前5页,later代表当前页的后5页
# range(当前页 - 5, 当前页 + 5 + 1)
"""
当总页数 < 11时,显示1-总页数
当总页数 > 11时:
如果当前页 <= 6:
1 - 11页
如果当前页 > 6:
如果: 当前页 + 5 > 总页数:
总页数 - 11, 总页数
else:
当前页 - 5, 当前页 + 5
"""
if self.all_pager < 11:
former = 1
later = self.all_pager
else:
if self.current_page <= 6:
former = 1
later = 11
else:
if (self.current_page + 5) > self.all_pager:
former = self.all_pager - 10
later = self.all_pager
else:
former = self.current_page - 5
later = self.current_page + 5
# 首页
first_page = "<a href='%s1'>首页</a>" % (base_url,)
list_page.append(first_page)
# 上一页 current_page - 1
if self.current_page == 1:
prev_page = "<a href='javascript:void(0);'>上一页</a>"
else:
prev_page = "<a href='%s%s'>上一页</a>" % (base_url, self.current_page - 1)
list_page.append(prev_page)
for p in range(former, later + 1):
if p == self.current_page:
tmp = "<a class='active' href='%s%s'>%s</a>" % (base_url, p, p)
else:
tmp = "<a href='%s%s'>%s</a>" % (base_url, p, p)
list_page.append(tmp)
# 下一页 current_page + 1
if self.current_page == self.all_pager:
next_page = "<a href='javascript:void(0);'>下一页</a>"
else:
next_page = "<a href='%s%s'>下一页</a>" % (base_url, self.current_page + 1)
list_page.append(next_page)
# 尾页
last_page = "<a href='%s%s'>尾页</a>" % (base_url, self.all_pager)
list_page.append(last_page)
# 页面跳转
jump = """<input type='text'/><a onclick="Jump('%s', this);">跳转</a>""" % ('/index/',)
script = """<script>
function Jump(baseUrl, arg){
var val = arg.previousElementSibling.value;
if(val.trim().length > 0){
location.href = baseUrl + val;
}
}
</script>"""
list_page.append(jump)
list_page.append(script)
str_page = "".join(list_page)
return str_page
class IndexHandler(tornado.web.RequestHandler):
def get(self, page):
page_obj = PageDivision(page, len(LIST_INFO))
current_list = LIST_INFO[page_obj.start:page_obj.end]
str_page = page_obj.page_str("/index/")
self.render("home/index.html", list_info=current_list, current_page=page_obj.current_page, str_page=str_page)
def post(self, page):
user = self.get_argument("username")
email = self.get_argument("email")
temp = {"username":user, "email":email}
LIST_INFO.append(temp)
self.redirect("/index/" + page)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pager a{
display:inline-block;
padding:5px;
margin:2px;
background-color:cornflowerblue;
}
.pager a.active{
background-color:red;
color:white;
}
</style>
</head>
<body>
<h1>提交数据</h1>
<form method="post" action="/index/{{current_page}}">
<input name="username" type="text">
<input name="email" type="text">
<input type="submit" value="提交">
</form>
<h1>显示数据</h1>
<table border="1">
<thead>
<tr>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
{% for line in list_info %}
<tr>
<td>{{line["username"]}}</td>
<td>{{line["email"]}}</td>
</tr>
{% end %}
</tbody>
</table>
<div class="pager">
{% raw str_page %}
</div>
</body>
</html>
4)二级域名路由
application = tornado.web.Application("123.abc.com$",[
(r"/index/(?P<page>\d*)", 123.IndexHandler)
])
8. Tornado模板引擎
1)基本使用:
if else for
2)继承extends,页面整体布局
模板html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pg-header{
height: 48px;
background-color:black;
}
.pg-footer{
height:100px;
background-color: #ddd;
}
</style>
{% block css %}{% end %}
</head>
<body>
<div class="pg-header"></div>
<div class="pg-content">
{% block body %}{% end %}
</div>
<div class="pg-footer">Footer</div>
<script src="aaa"></script>
{% block js %}{% end %}
</body>
</html>
Handler代码:
# -*-coding:utf-8-*-
import tornado.web
from core import Handler
class IndexHandler(Handler.MyHandler):
def get(self, *args, **kwargs):
self.render("extend/index.html", list_info=[1,2,3,4,5])
def post(self, *args, **kwargs):
pass
class Index2Handler(Handler.MyHandler):
def get(self, *args, **kwargs):
self.render("extend/index2.html", list_info=[11,22,33,44,55])
继承的页面:
{% extends '../master/layout.html'%}
{% block body %}
<h1>Index</h1>
{% include "../include/form.html"%}
{% end %}
{% block js %}
<script>
console.log("SSS");
</script>
{% end %}
主程序代码:
# -*-coding:utf-8-*-
import tornado.web
import tornado.ioloop
import home
import extend
settings = {
"template_path": "views", # 模板路径的配置
"static_path": "statics", # 静态文件
}
# 路由映射,基于正则的动态路由
application = tornado.web.Application([
(r"/index/(?P<page>\d*)", home.IndexHandler),
(r"/extend", extend.IndexHandler)
], **settings)
# application = tornado.web.Application("123.abc.com$",[
# (r"/index/(?P<page>\d*)", 123.IndexHandler)
# ])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
3)导入include,组件
常用的模块:
<form action="/">
<input type="text">
<input type="submit">
</form>
<ul>
{% for item in list_info %}
<li>{{item}}</li>
{% end %}
</ul>
引用常用模块的页面
{% extends '../master/layout.html'%}
{% block body %}
<h1>Index</h1>
{% include "../include/form.html"%}
{% end %}
{% block js %}
<script>
console.log("SSS");
</script>
{% end %}
9. Cookie详述
本质:浏览器端存放的键值对
特性:每次http请求都会携带
self.cookies 获取所有cookie
self.get_cookie("k1") 获取指定cookie
self.set_cookie("k1", "1") 设置cookie
1)在浏览器上使用javascript
document.cookie['k1']
document.cookie="k2=2" 新增cookie
document.cookie="k3=3;path=/;" 新增cookie并指定在某路径下使用
2)案例
// 设置cookie,指定秒数过期,超时时间是看不到的
function setCookie(name, value, expires){
var current_date = new Date(); //获取当前时间
current_date.setSeconds(current_date.getSeconds() + 5); //当前时间+5秒
document.cookie = name + "=" + value + ";expires=" + current_date.toUTCString();
}
参数:
- domain 指定域名下的cookie
- path 域名下制定url中的cookie
- secure https使用
注:jQuery中也有指定的插件jQuery Cookie专门用于操作cookie:$.cookie("k1","1", {"path":"", "domain":"", expires=7});
3)基于cookie作用户验证
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
if self.get_argument("user", None) in ["Steve", "John"]:
# self.set_cookie("name", self.get_argument("user", None))
self.set_secure_cookie("name", self.get_argument("user"))
else:
self.write("请登录")
class ManagerHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
if str(self.get_secure_cookie("name", None), encoding='utf-8') in ["Steve", "John"]:
self.write("欢迎登陆:" + str(self.get_secure_cookie("name", None), encoding='utf-8'))
else:
self.redirect("/index")
settings = {
"template_path": "views",
"static_path": "statics",
"cookie_secret": "dfjaouenbx",
}
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/manager", ManagerHandler),
], **settings)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
10. Session
自定义Session代码案例
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
container = {}
class Session:
def __init__(self, handler):
self.handler = handler
self.random_str = None
def __generate_random_str(self):
import hashlib
import time
obj = hashlib.md5()
obj.update(bytes(str(time.time())))
random_str = obj.hexdigest()
return random_str
def __setitem__(self, key, value):
# 在container中加入随机字符串
# 定义专属于自己的数据
# 在客户端中写入随机字符串
# 判断,请求的用户是否已有随机字符串
if not self.random_str:
random_str = self.handler.get_cookie("ran_str")
if not random_str:
random_str = self.__generate_random_str()
container[random_str] = {}
else:
# 客户端有随机字符串
# 如果服务端有对应的随机字符串
if random_str in container.keys():
pass
# 若没有则生成一个新的,并更新客户端的随机字符串
else:
random_str = self.__generate_random_str()
container[random_str] = {}
self.random_str = random_str
container[self.random_str][key] = value
for k,v in container.items():
print k, ":", v
self.handler.set_cookie("ran_str", self.random_str)
def __getitem__(self, key):
# 获取客户端的随机字符串
# 从container中获取专属于我的数据
# 专属信息[key]
random_str = self.handler.get_cookie("ran_str")
# 如果客户端没有则返回None
if not random_str:
return None
user_info_dict = container.get(random_str, None)
# 如果客户端没有随机字符串,返回None
if not user_info_dict:
return None
value = user_info_dict.get(key, None)
return value
class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.session = Session(self)
class IndexHandler(BaseHandler):
def get(self, *args, **kwargs):
if self.get_argument("user", None) in ["Steve", "John"]:
self.session["is_login"] = True
self.session["username"] = self.get_argument("user", None)
else:
self.write("请登录")
class ManagerHandler(BaseHandler):
def get(self, *args, **kwargs):
login = self.session["is_login"]
if login:
self.write(self.session["username"])
else:
self.write("失败")
settings = {
"template_path": "views",
"static_path": "statics",
"cookie_secret": "dfjaouenbx",
}
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/manager", ManagerHandler),
], **settings)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
11. Ajax
AJAX,Asynchronous JavaScript and XML(异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。
1)Ajax的使用场景:
注册时,输入用户名自动检测用户是否已经存在
登录时,提示用户名密码错误
删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除
2)伪Ajax,由于HTML标签的iframe标签具有局部加载内容的特性,所以可以用来伪造Ajax请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<p>请输入要加载的地址:<span id="currentTime"></span></p>
<p>
<input id="url" type="text"/>
<input type="button" value="刷新" onclick="LoadPage();">
</p>
</div>
<div>
<h2>加载页面位置</h2>
<iframe id="iframePosition" style="width:100%;height:500px;"></iframe>
</div>
<script type="text/javascript">
window.onload = function(){
var currentDate = new Date();
document.getElementById("currentTime").innerText = currentDate.getTime()
}
function LoadPage(){
var targetUrl = document.getElementById("url").value;
document.getElementById("iframePosition").src = targetUrl;
}
</script>
</body>
</html>
3)原生Ajax
Ajax就是使用XmlHttpRequest对象来完成请求的操作
代码案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>XMLHttpRequest - Ajax请求</h1>
<input type="button" onclick="XhrGetRequest();" value="Get发送请求">
<input type="button" onclick="XhrPostRequest();" value="Post发送请求">
<script src="jquery-3.2.1.js"></script>
<script>
function GetXHR(){
var xhr = null;
if(XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
function XhrPostRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接受到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data)
}
};
// 指定连接方式和地址--文件方式
xhr.open("POST", "/index", true);
// 设置请求头
xhr.setRequestHeader("Content-Type", "content-type","application/x-www-form-urlencoded; charset=UTF-8")
// 发送请求
xhr.send("n1=1; n2=2")
}
function XhrGetRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接受到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data)
}
};
// 指定连接方式和地址--文件方式
xhr.open("get", "/index", true);
// 发送请求
xhr.send();
}
</script>
</body>
</html>
4)jQuery发送Ajax请求
(1)导入jquery
(2)案例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>XMLHttpRequest - Ajax请求</h1>
<input type="button" onclick="XhrGetRequest();" value="Get发送请求">
<input type="button" onclick="XhrPostRequest();" value="Post发送请求">
<script src="jquery-3.2.1.js"></script>
<script>
function GetXHR(){
var xhr = null;
if(XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
function XhrPostRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接受到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data)
}
};
// 指定连接方式和地址--文件方式
xhr.open("POST", "/index", true);
// 设置请求头
xhr.setRequestHeader("Content-Type", "content-type","application/x-www-form-urlencoded; charset=UTF-8")
// 发送请求
xhr.send("n1=1; n2=2")
}
function XhrGetRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接受到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data)
}
};
// 指定连接方式和地址--文件方式
xhr.open("get", "/index", true);
// 发送请求
xhr.send();
}
$.get({
url: "地址",
data: {"k1":"v1",},
dataType: "xml", // 数据类型:text,json,xml,script,jsonp
success:function(da){
// obj = JSON.parse(da);
eval("<script>alert(123);</")
}
});
$.post({
url: "地址",
data: {"k1":"v1",},
dataType: "xml", // 数据类型:text,json,xml,script,jsonp
success:function(da){
// obj = JSON.parse(da);
eval("<script>alert(123);</")
}
});
$.getJSON({
url: "地址",
data: {"k1":"v1",},
success:function(da){
}
})
$.getScript({
url: "地址",
data: {"k1":"v1",},
success:function(da){
}
})
</script>
</body>
</html>
(3)部分参数:
url: 请求地址
type:请求方式,GET、POST
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认:"application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
“xml”:将服务器端返回的内容转换成xml格式
“text”:将服务器端返回的内容转换成普通文本格式
“html”:将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含javascript标签,则会尝试去执行。
“script”:尝试将返回只当作javascript去执行,然后再将服务器端返回的内容转换成普通文本格式
“json”:将服务器端返回的内容转换成相应的javascript对象
“jsonp”:JSONP格式,使用JSONP形式调用函数时,如“myurl?callback=?”jQuery将自动替换?为正确的函数名,以执行回调函数
(4)代码案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p><input type="button" onclick="XmlSendRequest();" value="Ajax请求"></p>
<script src="jquery-3.2.1.js"></script>
<script>
function XmlSendRequest(){
$.ajax({
url: "http://10.10.1.1:8888/test",
type: "POST",
data: {"k1":"v1",},
dataType: "text",
success: function(data){
console.log(data)
}
})
}
</script>
</body>
</html>
5)跨域Ajax
对于Ajax请求,如果跨域名请求,默认[浏览器]不允许
由于浏览器的同源策略,跨域Ajax不允许进行
Ajax允许有src的标签跨域,例如允许script、img、iframe块
(1)基于JSONP实现跨域Ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="Ajax" onclick="DoAjax();">
<input type="button" value="JSONPAjax" onclick="DoJSONPAjax();">
<script src="jquery-3.2.1.js"></script>
<script>
function DaAjax(){
$.ajax({
url: "/index",
type: "post",
data: {"k1": "v1"},
success: function(arg){
console.log(arg);
}
})
}
function DoJSONPAjax(){
// var tag = document.createElement("script");
// tag.src = "http://www.xiaohua.com";
// document.head.appendChild(tag);
// document.head.removeChild(tag);
$.ajax({
url: "",
dataType: "jsonp",
jsonpCallBack: "func"
})
}
</script>
</body>
</html>
(2)基于CORS实现跨域Ajax
本质:客户端不变,服务器端设置响应头即可
简单请求或非简单请求
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输
条件:
a. 请求方式:head、get、post
b. 请求头信息:
Accept
Accept-Language
Content-Language
Last-Event_ID
Content-Type 对应的值是以下三个中的任意一个
application/x-www-form-urlencoded
multipart/form-data
text/plain
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
代码案例:
import tornado.ioloop
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
pass
def post(self, *args, **kwargs):
pass
class CorsHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('{"status": 1, "message": "get"}')
def post(self, *args, **kwargs):
self.set_header("Access-Control-Allow-Origin", "http://")
self.write('{"status": 1, "message": "get"}')
def options(self, *args, **kwargs):
print("options")
# 允许put方法来
self.set_header("Access-Control-Allow-Methods", "*")
self.set_header("Access-Control-Allow-Methods", "PUT")
self.set_header("Access-Control-Allow-Origin", "http://www.xxx.com")
self.set_header("Access-Control-Allow-Headers", "k1, k2")
self.set_header("Access-Control-Max-Age", 10)
def put(self, *args, **kwargs):
self.set_header("Access-Control-Allow-Methods", "*")
print("put")
settings = {
"template_path": "views",
"static_path": "statics",
"static_url_prefix": "/statics/",
}
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/cors", CorsHandler),
], **settings)
6)跨域传输cookie
在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。如果想要发送:
浏览器端:XMLHttpRequest的withCredentials为true
服务器端:Access-Control-Allow-Credentials为true
注意:服务器端相应的Access-Control-Allow-Origin不能是通配符*
12. 文件上传
1)Form表单上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/index" method="post" enctype="multipart/form-data">
<input type="text" name="user">
<input type="checkbox" name="hobby" value="1">喜剧;
<input type="checkbox" name="hobby" value="2">动作;
<input type="checkbox" name="hobby" value="3">剧情;
<input type="file" name="fff">
<input type="submit" value="提交">
</form>
<ul>
{% for item in img_list %}
<li><img style="height:80px;width:50px;" src="/img/{{item}}"></li>
{% end %}
</ul>
</body>
</html>
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
IMG_LIST = []
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("index2.html", img_list=IMG_LIST)
def post(self, *args, **kwargs):
print self.get_argument("user")
print self.get_arguments("hobby")
file_metas = self.request.files.get("fff", None)
if not file_metas:
self.write("Invalid file")
self.redirect("index2.html")
for meta in file_metas:
file_name = meta["filename"]
import os
path = os.path.join("img", file_name)
with open(path, "wb") as f:
f.write(meta["body"])
IMG_LIST.append(file_name)
settings = {
"template_path": "views",
"static_path": "statics",
"static_url_prefix": "/statics/"
}
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/index2", IndexHandler)
], **settings)
if __name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
2)Ajax上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="file" id="img">
<input type="button" value="提交" onclick="UploadFile();">
<script src="jquery-3.2.1.js"></script>
<script>
// 通过ajax方式上传文件
function UploadFile(){
// 获取文件对象
var fileObj = document.getElementById("img").files[0];
// 创建form对象
var form = new FormData();
form.append("image", fileObj)
var xhr = new XMLHttpRequest();
xhr.open("post", "/index3", true);
xhr.send(form);
}
// 通过jquery方式上传文件
function UploadFile2(){
var fileObj = $("#img").files[0];
var form = new FormData();
form.append("image", fileObj);
$.ajax({
type: "POST",
url: "/index3",
data: form,
processData: false, // 必须加上这个参数
contentType: false, // 必须加上这个参数
success: function(arg){
console.log(arg);
}
})
}
</script>
</body>
</html>
3)iframe,解决ie等浏览器兼容性问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="my_form" name="form" action="/index4" method="post" enctype="multipart/form-data">
<div id="main">
<input name="file1" id="my_file" type="file">
<input type="button" name="action" value="Upload" onclick="Redirect();">
<iframe id="my_iframe" name="my_iframe" src="" class="hide"></iframe>
</div>
</form>
<script src="jquery-3.2.1.js"></script>
<script>
function Redirect(){
document.getElementById("my_iframe").onload = Test;
// 指定提交到iframe
document.getElementById("my_form").target = "my_iframe";
document.getElementById("my_form").submit();
}
function Test(arg){
var t = $("my_iframe").contents().find("body").text();
console.log(t);
</script>
</body>
</html>
13. 自定义Form表单验证
# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
import re
class IPField:
REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d\d)){3}$"
def __init__(self, error_dict=None, required=True):
# 封装了错误信息
self.error_dict = {}
if error_dict:
self.error_dict.update(error_dict)
self.required = required
self.error = None
self.is_valid = False
self.value = None
def validate(self, name, input_value):
"""
:param name: 字段名
:param input_value: 用户表单中输入的内容
:return:
"""
if not self.required:
# 用户输入可以为空
self.is_valid = True
self.value = input_value
else:
if not input_value.strip():
if self.error_dict.get("required", None):
self.error = self.error_dict["required"]
else:
self.error = "%s is required" % name
else:
ret = re.match(IPField.REGULAR, input_value)
if ret:
self.id_valid = True
self.value = ret.group()
else:
if self.error_dict.get("valid", None):
self.error = self.error_dict["valid"]
else:
self.error = "%s is invalid" % name
class FileField:
REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"
def __init__(self, error_dict=None, required=True):
# 封装了错误信息
self.error_dict = {}
if error_dict:
self.error_dict.update(error_dict)
self.required = required
self.error = None
self.is_valid = True
self.value = []
def validate(self, name, all_file_name_list):
"""
:param name: 字段名
:param input_value: 所有文件文件名
:return:
"""
self.name = name
if not self.required:
# 用户输入可以为空
self.is_valid = True
self.success_file_name_list = 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 required" % name
else:
# 循环所有的文件名
for file_name in all_file_name_list:
ret = re.match(FileField.REGULAR, file_name)
if not ret:
self.is_valid = False
if self.error_dict.get("valid", None):
self.error = self.error["valid"]
else:
self.error = "%s is invalid" % name
break
else:
self.success_file_name_list.append(file_name)
def save(self, request, path="statics/upload"):
# 所有文件列表
file_metas = request.files.get(self.name)
# 循环文件列表
temp_list = []
for meta in file_metas:
# 每个文件的文件名
file_name = meta["filename"]
import os
new_file_name = os.path.join(path, file_name)
if file_name and file_name in self.value:
temp_list.append(new_file_name)
with open(new_file_name, "wb") as f:
f.write(meta["body"])
self.value = temp_list
class BaseForm(object):
def check_valid(self, request):
# 获取用户提交的数据
# 将用户提交的数据和正则表达式匹配
flag = True
value_dict = {}
error_message_dict = {}
success_value_dict = {}
form_dict = self.__dict__
for key, regular in form_dict.items():
# key: ip...
# request: HomeIndex对象,self.get... self
# regular: IPFiled(required=True)
# input_value为用户输入的值
if type(regular) == FileField:
# 获取文件名
file_list = request.request.files.get(key)
# [{"body":"xx:, "filename":"x"}, {}]
input_value = []
for item in file_list:
input_value.append(item["filename"])
# 所有文件名进行验证
else:
input_value = request.get_argument(key)
# regular为IPField的对象
# 将具体的验证,放在IPField对象中
regular.validate(key, input_value)
if regular.is_valid:
success_value_dict[key] = regular.value
else:
error_message_dict[key] = regular.error
flag = False
value_dict[key] = input_value
return flag, success_value_dict, error_message_dict
class MainForm(BaseForm):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d\d)){3}$"
self.port = "(\d+)"
self.phone = "^1[3|4|5|8][0-9]\d{8}$"
class HomeForm(BaseForm):
def __init__(self):
self.host = "(.*)"
self.ip = IPField(required=True)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
def post(self, *args, **kwargs):
obj = MainForm()
is_valid, value_dict = obj.check_valid(self)
# 如果全部验证成功,将用户输入的所有数据放在obj.value_dict
if is_valid:
print value_dict
class HomeHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", error_dict=None)
def post(self, *args, **kwargs):
files = self.request.files.get("f",[])
# files=[文件1,文件2]
obj = HomeForm()
is_valid, success_dict, error_dict = obj.check_valid(self)
# 如果全部验证成功,将用户输入的所有数据放在obj.value_dict
if is_valid:
print "success", success_dict
else:
print "error", error_dict
self.render("home.html", error_dict=error_dict)
settings = {
"template_path": "views",
"static_path": "statics",
"static_url_prefix": "/statics/"
}
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/home", HomeHandler),
], **settings)
if __name__ == "__main__":
application.listen(9999)
tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/home" method="post">
<input type="text" name="ip" placeholder="ip">
<div>
<input type="file" name="f">
<input type="file" name="f">
</div>
{% if error_dict %}
<span>{{error_dict['ip']}}</span>
{% end %}
<input type="submit" value="submit">
</form>
</body>
</html>