python初识原生ajax,Python自动化开发学习24-Django中(AJAX)

原生Ajax

Ajax主要就是使用 XmlHttpRequest 对象来完成请求的操作,该对象在主流浏览器中均存在(除了早期的IE)。创建 XMLHttpRequest 对象的语法:

xmlhttp=new XMLHttpRequest();

老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:

xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");

XmlHttpRequest对象的主要方法

创建对象之后,就可以通过对象来调用下面的这些方法了:

void open(String method,String url,Boolen async) :创建请求

method :请求的方式,如:POST、GET、DELETE 等等

url :请求的地址

async :是否异步。一般都是异步的,只是他也支持同步的使用方式

void send(String body) :发送请求

body :要发送的数据

void setRequestHeader(String header,String value) :设置请求头

header :请求头的key

value :请求头的value

String getAllResponseHeaders() :获取所有响应头,返回值就是响应头数据

String getResponseHeader(String header) :获取响应头中指定header的值

header :响应头的key,返回值就是响应头的value

void abort() :终止请求

使用原生的方法发请求

发送GET请求

使用上面的方法,先发送一个空的GET请求:

document.getElementsByTagName('input')[0].onclick = function () {

var xmlhttp = new XMLHttpRequest();

xmlhttp.open('GET', '/ajax/');

xmlhttp.send();

};

为了能够真正有服务响应这个请求,还得写一个处理函数:

# views.py 文件

def ajax(request):

return render(request, 'ajax.html')

打开控制台,在网络里点击按钮触发事件后会看到我们发送的请求。点击这个请求可以看到标头。还有正文,正文里的响应正文返回的就是整个页面的html。并且整个过程里页面也是不会刷新的。

发送POST请求

上面发送的是GET请求,如果要发送POST请求,不只是要改一下method参数,还必须设置一下请求头:

xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');

另外还会有csrf的问题,csrf_token可以放到表单里,另外也可以设置到请求头中。

xmlhttp.setRequestHeader('X-CsrfToken', "{{ csrf_token }}");

之前一直用{% csrf_token %},这是生成一个html,这里使用的是{{ csrf_token }},直接就是token的字符串。

另外,客户端的Cookie里也会有一个csrf的值,看下来和{{ csrf_token }}的值是不同的,但是获取过来再同样放到请求头里也是可以通过csrf验证的:

xmlhttp.setRequestHeader('X-CsrfToken', getCookie('csrftoken'));

// 这里需要一个getCookie方法,有很多的实现方式,比如下面用正则匹配的

function getCookie(name) {

var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");

if(arr=document.cookie.match(reg)){

return arr[2];

} else {

return null;

}

}

XmlHttpRequest对象的主要属性

Number readyState :状态值

0-未初始化,尚未调用open()方法;

1-启动,调用了open()方法,未调用send()方法;

2-发送,已经调用了send()方法,未接收到响应;

3-接收,已经接收到部分响应数据;

4-完成,已经接收到全部响应数据;

Function onreadystatechange :当readyState的值改变时自动触发执行其对应的函数(回调函数)

String responseText :服务器返回的数据

XmlDocument responseXML :服务器返回的数据(Xml对象)

Number states :状态码(整数),如:200、404 等等

String statesText :状态文本(字符串),如:OK、NotFound 等等,对应上面的状态码的文字说明

补充一个知识点:关于状态码和状态文本,使用HttpResponse返回的时候也是可以设置的:

return HttpResponse(json.dumps(ret), status=404, reason="Not Found")

并且一般这个状态码返回的往往都是200,因为即使后台有错误,我们捕获或者验证处理了,之后还是会正常返回数据的。如果需要用到这种状态码,就像上面一样返回的时候带上status参数设置状态码。或者我们不要这种通用的状态码,而是在我们自己写的ret的字典里,也搞一套规则表示应用返回的状态信息。两种用法都有人用,而且貌似自己搞一套的更多。

兼容性的问题

解决兼容性的问题,只需要解决这一句代码就好了 var xmlhttp = new XMLHttpRequest(); 如果有这个对象,那么就使用这个对象,如果没有这个对象,就用另外一个对象。下面的function就是提供了一个返回正确的对象的方法:

function GetXmlhttp(){

var xmlhttp = null;

if(XMLHttpRequest){

xmlhttp = new XMLHttpRequest();

}else{

xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

}

return xmlhttp;

}

// 使用的时候就不是创建对象了,而是通过上面的方法获取到对象

// var xmlhttp = GetXmlhttp();

下面3句的效果是一样的,是讲兼容性的时候顺带提到的。即下面的3种方式引用都是可以的。

> XMLHttpRequest

< function XMLHttpRequest() { [native code] }:

> window.XMLHttpRequest

< function XMLHttpRequest() { [native code] }:

> window['XMLHttpRequest']

< function XMLHttpRequest() { [native code] }:

jQuery 的 Ajax

原生方法帮助我们了解原理,使用的话还是jQuery会方便的多。

回调函数的参数-获取原生的XMLHttpRequest对象

之前使用回调函数success的时候,只用到了一个参数。这个回调函数最多是有3个参数的:

如果只传递一个参数,表示只请求服务器响应的文本信息,这样可以根据需求在服务器设置一个json格式的文本信息,客户端就可以直接获取到服务端的数据。

如果传递两个参数,则在第一个参数的基础上,增加了一个状态参数。比如:会返回字符串"success",貌似也没什么用。

如果传递三个参数,则第三个参数就是完整的ajax相应的状态信息。就是XMLHttpRequest对象,拿来就能按原生的方法来操作。

下面上传文件的小节里会有例子

伪Ajax请求

由于HTML标签的iframe标签具有局部加载内容的特性,所以可以使用其来伪造Ajax请求。

iframe标签

在标签内部加上一个src的属性,就会在这个元素的内部创建包含另外一个文档的内联框架(就是嵌套一个网页):

注意带上前面的http://

举例:https://blog.51cto.com/steed

URL:

function iframeRequest() {

var url = $('#url').val();

$('iframe').attr('src', url);

}

上面还附带了一个方法,跟上input框的url,刷新iframe标签里嵌套的页面。但是页面整体是不刷新的,只有嵌套框架的内部会变化。上面的例子要说明的问题是:iframe标签也可以实现不刷新页面发送请求并且拿到返回的数据(偷偷的发请求)。

接下来,在上面的基础上,现在在form里写一个iframe标签,并且通过target属性和iframe的name相关联。原本页面上的form请求,现在都在iframe里实现了不刷新页面的提交和数据返回:

{% csrf_token %}

下面是对应的处理函数:

# views.py 文件

import time

def ajax(request):

if request.method == 'GET':

return render(request, 'ajax.html')

elif request.method == 'POST':

ret = {'code': True, 'data': request.POST.get('username')}

time.sleep(3)

return HttpResponse(json.dumps(ret))

获取返回值

上面虽然返回了值,但是是在页面上显示的,如何拿到这些数据。iframe内部是一个完整的Document对象,这里需要使用一个特殊的方法才能取到里面的值。另外,iframe内部是在提交并且返回数据只会才会有我们需要的值的,提交之后立刻获取也是获取不到的,需要等到数据返回后才能获取到。上面的处理函数里加了一个sleep,效果更佳明显。

获取数据的时机

iframe的数据加载完成后,会触发一个onload事件,给onload事件绑定一个函数,此时再去获取,就能获取到标签内部最新的值。不过这样还不完美,第一次加载页面的时候也会触发onload事件,要解决这个问题,需要为submit绑定事件,通过submit事件来给iframe标签绑定onload事件:

document.getElementsByTagName('form')[0].onsubmit = function () {

document.getElementsByTagName('iframe')[0].onload = function () {

alert(123)

};

}

获取数据的方法

既然iframe内部是一个DOM对象,使用 .contentWindow.document 就是内层的DOM对象。之后就是之前学习的知识了:

document.getElementsByTagName('form')[0].onsubmit = function () {

document.getElementsByTagName('iframe')[0].onload = function () {

var text = this.contentWindow.document.getElementsByTagName('body')[0].innerText;

alert(text);

};

}

如果是用jQuery的话,就使用jQuery的方法:

$('form').submit(function () {

$('iframe').load(function () {

var text = $(this).contents().find('body').text();

alert(text);

})

})

上传文件

之前通过Ajax发送的都是普通数据。发送普通数据的时候,推荐还是用jQuery,退而求其次是用原生的,用伪Ajax并不方便。

下面看看不普通的数据,就是上传文件。

好看的上传按钮

下面的input是系统自带的上传按钮:

Title

.upload{display: inline-block; padding: 10px; background-color: blue; color: white;}

上传

上传按钮在不用的浏览器里看到的样子也是不同的,并且样式并不能完全的按自己的需要来定制。如果要做一个好看的上传按钮,需要特殊处理一下。

Title

div.file{position: relative; width: 100px; height: 50px; line-height: 50px;}

div.file>#file{position: absolute; width:100%; height:100%; z-index: 20; opacity: 0;}

div.file>.upload{position: absolute; width:100%; height:100%; z-index: 10;

display: inline-block; background-color: blue; color: white; text-align: center;}

上面的思路就是,把默认的input和我们的标签重叠。把默认的input放在上面,但是透明度设置为0就是看不见。把我们自定制的标签放在下面,但是上层由于全透明看不见,看到的效果就是我们自定制标签的效果。但是点击的效果还是点击了默认的input按钮,因为它才是真正在上层的元素,只是看不见而已。

要自定制好看的上传按钮,基本都是基于这个方式来实现的。

FormData 对象

这里需要先引入一个FormData对象。FormData对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。这个对象有一个append()方法,为当前的FormData对象添加键值对:

void append(String name, String value);

void append(String name, Blob value, optional String filename);

第一种用法就是传入2个字符串,前面是Key,后面是字符串。

第二种用法是,第二个参数传入一个Blob对象,即一个不可变、原始数据的类文件对象。简单理解成文件对象就好了,在上面的例子中,通过 var file_obj = document.getElementById('file').files[0]; 就能获取到这个文件对象。

最后还有一个可选的(optional)第三个参数,指定文件的文件名。

原生Ajax上传

页面的代码如下,主要看js的部分:

Title

div.file{position: relative; width: 100px; height: 50px; line-height: 50px;}

div.file>#file{position: absolute; width:100%; height:100%; z-index: 20; opacity: 0;}

div.file>.upload{position: absolute; width:100%; height:100%; z-index: 10;

display: inline-block; background-color: blue; color: white; text-align: center;}

document.getElementById('btn').onclick = function () {

var file_obj = document.getElementById('file').files[0];

var form_data = new FormData();

form_data.append('username', 'root');

form_data.append('file', file_obj);

var xmlhttp = new XMLHttpRequest();

xmlhttp.open('POST', '/upload/');

xmlhttp.setRequestHeader('X-CsrfToken', "{{ csrf_token }}");

// form_data.append('csrfmiddlewaretoken', "{{ csrf_token }}"); // 也可以加在form里

xmlhttp.onreadystatechange = function () {

if (xmlhttp.readyState === 4){

var obj = JSON.parse(xmlhttp.responseText);

console.log(obj);

}

};

xmlhttp.send(form_data)

}

对应后端的处理函数:

# views.py 文件

def upload(request):

if request.method == 'GET':

return render(request, 'upload.html')

elif request.method == 'POST':

username = request.POST.get('username')

file_obj = request.FILES.get('file')

print(type(file_obj), file_obj) # 从这里看到已经收到文件了

print(file_obj.__dict__) # 看看有哪些属性,比如:文件名、大小、文件类型

ret = {'code': True, 'data': username}

# 保存文件

with open(file_obj.name, 'wb') as file:

for item in file_obj.chunks():

file.write(item)

return HttpResponse(json.dumps(ret))

jQuery 的 Ajax 上传

只有js的部分和上面不同:

$('#btn').click(function () {

var file_obj = $(':file')[0].files[0];

var form_data = new FormData();

form_data.append('username', 'root');

form_data.append('file', file_obj);

$.ajax({

url: '/upload/',

type: 'POST',

data: form_data,

headers: {'X-CsrfToken': "{{ csrf_token }}"},

processData: false, // 上传文件必须要有这句,告诉jQuery不要对data进行加工

contentType: false, // 上传文件必须要有这句,告诉jQuery不要设置contentType

success: function (data, textStatus, jqXHR) {

console.log(data);

console.log(textStatus);

console.log(jqXHR);

}

})

})

伪Ajax上传文件

上面使用Ajax上传文件,都必须依赖 FormDate 对象。但是这个对象不是所有浏览器都支持的,没错,还是老版的IE。要考虑兼容性问题,就需要用到这里的伪Ajax。

使用伪造Ajax上传文件和上传一般的内容几乎没什么差别,加一个 type="file" 的input框。

注意:form标签要上传文件,需要加上 enctype="multipart/form-data" 这个属性

{% csrf_token %}

$('form').submit(function () {

$('iframe').load(function () {

var text = $(this).contents().find('body').text();

var obj = JSON.parse(text);

console.log(obj);

})

})

上面还多了一个iframe的大框,设置 style="display: none;" 即可。剩下的上传按钮的美化方法应该是一样的。

这个方法的兼容性是最高的,所以伪Ajax在上传文件的应用场景中是推荐使用的方法。

生成预览

如果上传的文件是张图片,可以生成预览。把上传的图片存放到一个存放静态文件的目录里,setting.py里也设置好静态文件的目录。比如:

STATICFILES_DIRS = (

os.path.join(BASE_DIR, 'static'),

)

这里预览的图片是从服务器上获取的。也就是先把图片上传的服务器,然后服务器端返回文件在服务器上的路径给预览,预览再通过路径拿到服务器端的文件,最后显示在页面上。

处理函数修改一下,默认是保存到根目录的,现在保存到专门的目录里:

# views.py 文件

import os

def upload(request):

if request.method == 'GET':

return render(request, 'upload.html')

elif request.method == 'POST':

username = request.POST.get('username')

file_obj = request.FILES.get('file')

# print(type(file_obj), file_obj) # 从这里看到已经收到文件了

# print(file_obj.__dict__) # 看看有哪些属性,比如:文件名、大小、文件类型

img_path = os.path.join('static/imgs/', file_obj.name) # 生成文件保存的路径

ret = {'code': True, 'data': username, 'img': img_path} # 返回的信息要有文件的路径

# 保存文件

with open(img_path, 'wb') as file:

for item in file_obj.chunks():

file.write(item)

return HttpResponse(json.dumps(ret))

主要的变化就是生成了文件保存的路径,另外将路径返回给客户端。

下面是html的完整代码:

{% csrf_token %}

$('form').submit(function () {

$('iframe').load(function () {

var text = $(this).contents().find('body').text();

var obj = JSON.parse(text);

// console.log(obj);

var imgTag = document.createElement('img');

imgTag.src = "/" + obj.img; // 这里注意前面需要添加个'/'

$('#preview').empty(); // 可能有上次的预览,要先清空

$('#preview').append(imgTag);

})

})

上面预设了一个图片预览的div。Ajax请求返回后,获取到其中的图片路径(这个路径应该是直接拼接到“127.0.0.1:8000”后面就能访问到图片的)。创建一个img标签,设置好src,然后加到预览的div里。加进去之前,要把div清空一下,因为有上一次预览加进去的img标签

一步提交

上面的上传文件都是分两步上传的,首先是input[type='file']标签选择文件,然后是submit或者button按钮绑定事件来进行提交。

这里也可以不要后面的submit或者button按钮,为input[type='file']绑定一个onchange事件触发一个form表单的submit事件,或者是替换到原来button按钮的onclick事件。前一种的实现只要加上下面的这个事件绑定:

$(':file').change(function () {

$('form').submit();

});

补充

再来补充点相关的小点

处理函数识别AJAX请求

如下面的例子,使用 is_ajax() 方法就可以识别出请求是否是AJAX请求了:

def ajax_test(request):

if request.is_ajax():

message = "This is ajax"

else:

message = "Not ajax"

return HttpResponse(message)

上传文件的插件推荐

这是一个可以拖拽上传文件的插件, dropzone.js 。

官网:http://www.dropzonejs.com/

下载文件

import os, tempfile, zipfile

from django.http import HttpResponse

from django.core.servers.basehttp import FileWrapper

def send_file(request):

"""

Send a file through Django without loading the whole file into

memory at once. The FileWrapper will turn the file object into an

iterator for chunks of 8KB.

"""

filename = __file__ # Select your file here.

wrapper = FileWrapper(open(filename, 'rb'))

response = HttpResponse(wrapper, content_type='text/plain')

response['Content-Length'] = os.path.getsize(filename)

return response

def send_zipfile(request):

"""

Create a ZIP file on disk and transmit it in chunks of 8KB,

without loading the whole file into memory. A similar approach can

be used for large dynamic PDF files.

"""

temp = tempfile.TemporaryFile()

archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)

for index in range(10):

filename = __file__ # Select your files here.

archive.write(filename, 'file%d.txt' % index)

archive.close()

wrapper = FileWrapper(temp)

response = HttpResponse(wrapper, content_type='application/zip')

response['Content-Disposition'] = 'attachment; filename=test.zip'

response['Content-Length'] = temp.tell()

temp.seek(0)

return response

之后讲到项目,用的下载文件的方法,也是抄的这段代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值