原生AJAX
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)
1、XmlHttpRequest对象介绍 (不兼容IE老版本)
x = new XMLHttpRequest()
XmlHttpRequest对象的主要方法:
格式说明:
返回值 方法名(参数,...)
a. void open(String method,String url,Boolen async)
用于创建请求
method: 请求方式(字符串类型),如:POST、GET、DELETE...
url: 要请求的地址(字符串类型)
async: 是否异步(布尔类型)
b. void send(String body)
用于发送请求
body: 要发送的数据(字符串类型)
c. void setRequestHeader(String header,String value)
用于设置请求头
header: 请求头的key(字符串类型)
vlaue: 请求头的value(字符串类型)
d. String getAllResponseHeaders()
获取所有响应头
响应头数据(字符串类型)
e. String getResponseHeader(String header)
获取响应头中指定header的值
header: 响应头的key(字符串类型)
响应头中指定的header对应的值
f. void abort()
XmlHttpRequest对象的主要属性:
a. Number readyState
状态值(整数)
0-未初始化,尚未调用open()方法;
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...
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/jquery-2.2.3.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
</head>
<button value="XML_SEND" onclick="xmlSend();">XMLHttpRequest()</button>
</div>
<script>
function xmlSend(){
let xml = new XMLHttpRequest(); //创建XMLHttpResquest对象
xml.open('POST','/ajax_original/'); //设置请求地址
xml.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken') ); //设置请求头
{#xml.send(); //发起请求#}
xml.onreadystatechange = function () { //状态改变
console.log('xml 实例readyState状态改变:',xml.readyState);
if (xml.readyState === 4) {
console.log('收到返回值字符串:',xml.responseText);
console.log('转换为对象:',JSON.parse(xml.responseText));
console.log('服务端返回status状态码:',xml.status);
console.log('服务端返回statusText文本:',xml.statusText);
//测试发送body数据,需要设置请求头Content-Type(告知服务器数据格式,服务器用对应的方式解析)
xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
xml.send('name=name1; pwd=123');
</script>
</body>
</html>
django-views.py函数处理
from django.shortcuts import HttpResponse
from django.views import View
import json
# Create your views here.
class AjaxOrnginal(View):
def get(self,request):
return render(request, 'ajax_templates/original_xmlHttpRequest.html')
def post(self, request):
print(request.POST)
ret = {'code': True, 'data': None}
# return HttpResponse(json.dumps(ret))
return HttpResponse(json.dumps(ret),status=404, reason="Oh Fuck ! EveryThing is None")
2、兼容IE老版本XmlHttpRequest对象
<h1>XMLHttpRequest - Ajax请求</h1>
<input type="button" onclick="XmlGetRequest();" value="Get发送请求" />
<input type="button" onclick="XmlPostRequest();" value="Post发送请求" />
<script src="/statics/jquery-1.12.4.js"></script>
<script type="text/javascript">
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', "/test/", true);
// 设置请求头
xhr.setRequestHeader('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', "/test/", true);
// 发送请求
xhr.send();
</script>
</body>
"伪"AJAX
由于HTML标签的iframe标签具有局部加载内容的特性,所以可以使用其来伪造Ajax请求。
<form action="/ajax_original/" method="post" target="ifra">
<iframe name="ifra" src="https://www.baidu.com"></iframe>
<input type="text" name="username">
<input type="text" name="email">
<input type="submit" value="伪Ajax提交">
</form>
ifram对象为一个小的新html标签窗口,获取ifram对象的内容需要从这个子窗口document对象
$("#ifra").contents().find('body').text()
试用场景:
普通字符串、字典数据:优先使用用Ajax,其次XMLHttpRequest,再次考虑iframe
上传文件的三种方法
1、原生XMLHttpRequest ---- 上传文件
依赖:
new FormData(); 对象,
html前端:XMLHttpRequest()
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/jquery-2.2.3.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
</head>
<div class="upload_div">
<input type="file" value="上传" class="file" id="original_upload_input" name="file1">
</div>
<input type="button" onclick="upLoadFile();" value="上传">
</div>
<script>
function upLoadFile(){
let file_obj = document.getElementById('original_upload_input').files[0];
let fd = new FormData();
fd.append('username', 'root');
fd.append('file1', file_obj);
let xhr = new XMLHttpRequest();
xhr.open('post', '/xhr_upload/');
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken') ); //设置请求头
xhr.onreadystatechange = function () { //状态改变
console.log('xml 实例readyState状态改变:',xhr.readyState);
if (xhr.readyState === 4) {
console.log('收到返回值字符串:',xhr.responseText);
xhr.send(fd);
</script>
</body>
</html>
django views函数后台处理
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.views import View
import json
# Create your views here.
class XhrUpload(View):
def get(self,request):
return render(request, 'ajax_templates/original_upload.html')
def post(self, request):
file = request.FILES.get('file1')
with open(file.name, 'wb') as f:
for item in file.chunks():
f.write(item)
return HttpResponse('upload ok')
2、jQuery ---- 上传文件
仍然使用new FormData(); 对象
需增加特殊设置:
processData:false, // tell jQuery not to process the data:不要转义数据
contentType:false, // tell jQuery not to set contentType:不要设置请求头
let file_obj = document.getElementById('original_upload_input').files[0];
// 仍然要依赖于FormData()对象,并且要设置processData和contentType参数
let fd = new FormData();
fd.append('username', 'root');
fd.append('file1', file_obj);
//使用ajax 上传数据
$.ajax({
url:'/xhr_upload/',
type: 'POST',
data:fd,
headers:{'X-CSRFtoken': $.cookie('csrftoken')},
processData:false, // tell jQuery not to process the data:不要转义数据
contentType:false, // tell jQuery not to set contentType:不要设置请求头
success:function (arg,a1,a2) {
console.log(arg); //返回字符串
console.log(a1); //状态文本
console.log(a2); //XHR对象
3、ifram 上传文件
优势:可以将上传后的返回值信息做成图片预览等功能。
<form action="/xhr_upload/" method="post" enctype="multipart/form-data" target="ifram1">
{% csrf_token %}
<iframe id="ifram1" name="ifram1"></iframe>
<input type="file" name="file1">
<input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)">
</form>
ajax 从iframe 加载成功后 获取返回值:$('#iframe标签').load(function(){xxxxxx; ......; })
function iframSubmit() {
//上传成功,iframe标签加载返回值成功,从标签中获取元素
$('#ifram1').load(function () {
//获取返回值,
let recv = $('#ifram1').contents().find('body').text();
console.log(recv);
django views.py函数处理
class XhrUpload(View):
def get(self,request):
return render(request, 'ajax_templates/original_upload.html')
def post(self, request):
file = request.FILES.get('file1')
with open(file.name, 'wb') as f:
for item in file.chunks():
f.write(item)
ret = HttpResponse('upload ok')
ret['X-Frame-Options'] = 'ALLOW'
return ret
图片预览版本iframe:
步骤说明:当iframe标签加载成功.onload(function(){})时,获取iframe标签中 的返回值。返回值中包含图片uri, 通过组合'/' + uri 获取图片路径,创建img标签,在浏览器中增加此标签实现图片自动get展示
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/jquery-2.2.3.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<style>
.hide{
display: block;}
</style>
</head>
<form action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1">
{% csrf_token %}
<iframe id="ifram1" name="ifram1" class="hide"></iframe>
<input type="file" name="file1">
<input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)">
</form>
</div>
<div id="preview_div"></div>
<script>
//上传成功,iframe标签加载返回值成功,从标签中获取元素
$('#ifram1').load(function () {
//获取返回值,返回的是json.dumps()序列化的字符串也可以获取
let recv = $('#ifram1').contents().find('body').text();
let data = JSON.parse(recv);
console.log(data);
//获取返回的图片URI,并生成图片标签
let imgTag = document.createElement('img');
imgTag.src = "/" + data.uri;
//清空已有img图片标签
$('#preview_div').empty();
//添加dom元素
$('#preview_div').append(imgTag);
</script>
</body>
</html>
如需选择文件后自动上传,js绑定onchange事件实现上传:
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/jquery-2.2.3.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<style>
.hide{
display: none;}
</style>
</head>
<form id="upload_form" action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1">
{% csrf_token %}
<iframe id="ifram1" name="ifram1" class="hide"></iframe>
<input type="file" name="file1" onchange="upLoadFile()">
<input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)">
</form>
</div>
<div id="preview_div"></div>
<script>
//选择文件后自动上传
function upLoadFile(){
$('#upload_form').submit()
//上传成功,iframe标签加载返回值成功,从标签中获取元素
$('#ifram1').load(function () {
//获取返回值,返回的是json.dumps()序列化的字符串也可以获取
let recv = $('#ifram1').contents().find('body').text();
let data = JSON.parse(recv);
console.log(data);
//获取返回的图片URI,并生成图片标签
let imgTag = document.createElement('img');
imgTag.src = "/" + data.uri;
//清空已有img图片标签
$('#preview_div').empty();
//添加dom元素
$('#preview_div').append(imgTag);
</script>
</body>
input 标签绑定onchange事件;选择文件后自动上传
ifram 优势:在所有浏览器都支持,不存在兼容性问题。而XMLHTTPRequest低版本IE浏览器不支持。
应用案例(点击更换头像):
说明:input标签 属性title='xxxx' 为设置鼠标移入的时候显示的提示语设置
原理:input标签100%占用div;并且透明度为0 即肉眼不可见。img标签和<input type="file">标签重叠在底层显示。
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/jquery-2.2.3.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<style>
.hide{
display: none;}
.preview_div{
position: relative;
overflow: hidden;
display: inline-block;
.head_portrait{
width: 108px;
.head_img {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
opacity: 0;
z-index: 2;
</style>
</head>
<form id="upload_form" action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1">
{% csrf_token %}
<iframe id="ifram1" name="ifram1" class="hide"></iframe>
<div id="preview_div" class="preview_div">
<img id="head_portrait" class="head_portrait" src="/static/images/default.jpg" >
<input class="head_img" type="file" name="file1" title="点击上传头像" onchange="upLoadFile()" >
</div>
<input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)">
</form>
</div>
<script>
//选择文件后自动上传
function upLoadFile(){
$('#upload_form').submit()
//上传成功,iframe标签加载返回值成功,从标签中获取元素
$('#ifram1').load(function () {
//获取返回值,返回的是json.dumps()序列化的字符串也可以获取
let recv = $('#ifram1').contents().find('body').text();
let data = JSON.parse(recv);
console.log(data);
// 在原有图片上面更新
let uri = "/" + data.uri;
$('#head_portrait').attr('src', uri);
</script>
</body>
</html>
-----------------------------------------------------------
图片验证码案例
验证码图片生成
工具:pip3 install pillow
utils/check_code.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import random
# pip3 install pillow
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,
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
django views.py 后端使用如上check_code.py 生成图片
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.views import View
import json
import os
from io import BytesIO
from utils.check_code import create_validate_code
# Create your views here.
# 生成图片验证码并将对应字符串存储到session中
class CheckCodeImage(View):
def get(self, request):
# from io import BytesIO ,开辟内存空间,用来存储字节流
stream = BytesIO()
# 使用自定义函数创建验证码图片&字符串
img, code_str = create_validate_code()
# 将图片保存到内存的字节流中
img.save(stream, 'PNG')
# 将验证码字符串保存到session中
request.session['CheckCode'] = code_str
# 将图片字节流返回给客户端:HttpResponse()可以返回字节
return HttpResponse(stream.getvalue())
# 登录请求校验验证码
class LoginCaptch(View):
def get(self, request):
return render(request, 'ajax_templates/login_iframe_captcha.html')
def post(self, request):
if request.session['CheckCode'] == request.POST.get('CheckCode'):
print('验证码正确')
else:
print('验证码错误:',request.session['CheckCode'].upper(),request.POST.get('CheckCode'))
return render(request, 'ajax_templates/login_iframe_captcha.html')
urls.py
url(r'iframe_login_captch/', ajax_views.LoginCaptch.as_view()),
url(r'^check_code.html$', ajax_views.CheckCodeImage.as_view()),
login_iframe_captcha.html 前端展示验证码图片
点击图片本身,自动刷新验证码:请求url增加? 浏览器检测到url变化,重新发起请求,后端处理不变。
login_iframe_captcha.html
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Title</title>
<style>
.login{
display: inline-block;
</style>
</head>
<form action="/iframe_login_captch/" method="post">
{% csrf_token %}
<div class="login">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" >
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="pwd" placeholder="请输入密码" >
</div>
<div class="form-group">
<label for="captcha">验证码</label>
<input type="text" class="form-control" id="captcha" name="CheckCode" placeholder="验证码" >
</div>
<button type="submit">登 陆</button>
</div>
</form>
<img src="/check_code.html" onclick="changeCheckCode(this)">
</div>
<script>
//点击图片改变图片验证码: 请求url增加? 浏览器检测到url变化,重新发起请求,后端处理不变。
function changeCheckCode(ths) {
ths.src = ths.src + "?";
</script>
</body>
</html>
---------------------------------------------------------------------