一、什么是Ajax技术?
异步的JavaScript和XML.使用Javascript语言与服务器进行异步交互,传输的数据为XML(更多的使用json数据).Ajax不是一门新的编程语言,而是一种使用现有标准的一套方法.
- XMLHttpRequest 对象 (异步的与服务器交换数据)
- JavaScript/DOM (信息显示/交互)
- CSS (给数据定义样式)
- XML (作为转换数据的格式)
优点:
在不重新加载真个页面的情况下,可以与服务器交换数据并且更新部分网页内容.(这个特点让用户在不知不觉中完成请求和响应).并且Ajax不需要任何插件,但是需要用户允许JavaScript在浏览器上执行.
(1)同步交互:客户端发送一个请求后,需要等待服务器响应结束后,才能发出第二个请求,是串行的.
(2)异步交互:客户发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求.
二、基本格式
使用Ajax的请求是基于onclick事件的(post)
当按钮被点击时,它负责调用名为 loadXMLDoc() 的函数:
<div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div>
<button type="button" onclick="loadXMLDoc()">修改内容</button>
Ajax请求格式:
<head>
<script>
function loadXMLDoc()
{
.... AJAX 脚本执行 ...
}
</script>
</head>
一套完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/try/ajax/demo_get.php",true);
xmlhttp.send();
}
</script>
</head>
<body> <h2>AJAX</h2>
<button type="button" onclick="loadXMLDoc()">请求数据</button>
<div id="myDiv"></div> </body>
</html>
三、创建XMLHTTP Response对象
XMLHTTP Response是Ajax的基础,用于在后台与服务器进行交换数据。意味着可以不加载真个页面的情况下,对页面的某部分进行更新。
创建语句:
variable=new XMLHttpRequest(); 为了应对所有的现代浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。
如果支持,则创建 XMLHttpRequest 对象。如果不支持,则创建 ActiveXObject : var xmlhttp;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest(); }
else {
// IE6, IE5 浏览器执行代码 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
四、向服务器发送请求
XMLHttpResponse 对象用于和服务器交换数据。
向服务器发送请求会使用两个方法open()和send()
xmlhttp.open('GET',"ajax_info.text",true); #open(method,url,async)
method : 请求的类型: POST或GET
url : 文件在服务器上的位置
async : true(异步)或者 false(同步)
xmlhttp.send(); #send(string)
string: 仅用于POST请求
服务器上的文件是任何类型的文件,可以是文本.txt和.xml或者脚本文件如.asp和.php(在传回响应值前,能够在服务器上执行任务)
XMLHttpResponse对象如果要用于AJAX的话,其open()方法的async参数必须设置为teue:
xmlhttp.open("GET","ajax_test.html",true);
通过 AJAX,JavaScript 无需等待服务器的响应,而是:
(1)在等待服务器响应时执行其他脚本
(2)当响应就绪后对响应进行处理
当使用 async=true 时,请规定在响应处于 onreadystatechange 事件中的就绪状态时执行的函数.
如需使用 async=false,请将 open() 方法中的第三个参数改为 false,如果对一些小型请求也是可以的,但是JavaScript会等到服务器响应就绪后才继续执行.如果服务器繁忙或者缓慢,应用程序会挂起或者停止. 当async=false 时,不要编写 onreadystatechange 函数 - 把代码放到 send() 语句后面即可
xmlhttp.open("GET","/try/ajax/ajax_info.txt",false);
xmlhttp.send();
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
五、来自服务器的响应
如果需要获取来自服务器的响应,请使用XMLHTTP Response对象的responseText或者responseXML属性。
1 属性:responseText 描述:获取字符串形式的响应数据
2 属性:responseXML 描述:获取XML形式的响应数据
六、Ajax参数
请求参数:
######################------------data---------################ data: 当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格式
(urlencoded:?a=1&b=2)发送给服务端;此外,ajax默认以get方式发送请求。 function testData() {
$.ajax("/test",{ //此时的data是一个json形式的对象
data:{
a:1,
b:2
}
}); //?a=1&b=2
######################------------processData---------################ processData:声明当前的data数据是否进行转码或预处理,默认为true,即预处理;if为false,
那么对data:{a:1,b:2}会调用json对象的toString()方法,即{a:1,b:2}.toString()
,最后得到一个[object,Object]形式的结果。 ######################------------contentType---------################ contentType:默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。
用来指明当前请求的数据编码格式;urlencoded:?a=1&b=2;如果想以其他方式提交数据,
比如contentType:"application/json",即向服务器发送一个json字符串:
$.ajax("/ajax_get",{ data:JSON.stringify({
a:22,
b:33
}),
contentType:"application/json",
type:"POST", }); //{a: 22, b: 33} 注意:contentType:"application/json"一旦设定,data必须是json字符串,不能是json对象 views.py: json.loads(request.body.decode("utf8")) ######################------------traditional---------################ traditional:一般是我们的data数据有数组时会用到 :data:{a:22,b:33,c:["x","y"]},
traditional为false会对数据进行深层次迭代;
复制代码
响应参数: 复制代码
dataType: 预期服务器返回的数据类型,服务器端返回的数据会根据这个值解析后,传递给回调函数。
默认不需要显性指定这个属性,ajax会根据服务器返回的content Type来进行转换;
比如我们的服务器响应的content Type为json格式,这时ajax方法就会对响应的内容
进行一个json格式的转换,if转换成功,我们在success的回调函数里就会得到一个json格式
的对象;转换失败就会触发error这个回调函数。如果我们明确地指定目标类型,就可以使用
data Type。
dataType的可用值:html|xml|json|text|script
见下dataType实例
七、Ajax请求设置csrf——token
方式一
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "chao",
"password": 123456,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
},
success: function (data) {
console.log(data);
}
})
方式二
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式三
通过获取返回的cookie中的字符串 放置在请求头中发送。
注意:需要引入一个jquery.cookie.js插件。
<script src="{% static 'js/jquery.cookie.js' %}"></script> $.ajax({ headers:{"X-CSRFToken":$.cookie('csrftoken')}, #其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加 })
$.ajax({
url: "/cookie_ajax/",
type: "POST",
headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrftoken,并设置到请求头中
data: {"username": "chao", "password": 123456},
success: function (data) {
console.log(data);
}
})
更多细节详见:Django官方文档中关于CSRF的内容
八、Ajax文件上传
请求头ContentType
ContentType指的是请求体的编码类型,常见的类型共有3种:
1 application/x-www-form-urlencoded(看下图)
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype
属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。请求类似于下面这样(无关的请求头在本文中都省略掉了):
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8 user=yuan&age=22 #这就是上面这种contenttype规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,都是这样拼接数据,大家公认的一种数据格式,但是如果你contenttype指定的是urlencoded类型,但是post请求体里面的数据是下面那种json的格式,那么就出错了,服务端没法解开数据。
看network来查看我们发送的请求体:
点击一下上面红框的内容,你就会看到,这次post请求发送数据的原始格式
2 multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype
等于 multipart/form-data,form表单不支持发json类型的contenttype格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。直接来看一个请求示例:(了解)
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user" chao
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary
开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary--
标示结束。关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype
属性指定,默认为 application/x-www-form-urlencoded
。其实 enctype
还支持 text/plain
,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。
3 application/json
application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。记得以前做过一个项目时,需要提交的数据层次非常深,我就是把数据 JSON 序列化之后来提交的。不过当时我是把 JSON 字符串作为 val,仍然放在键值对里,以 x-www-form-urlencoded 方式提交。
如果在ajax里面写上这个contenttype类型,那么data参数对应的数据,就不能是个object类型数据了,必须是json字符串,contenttype:'json',简写一个json,它也能识别是application/json类型
服务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据,其实就是服务端框架已经写好了针对这几个类型的不同的解析数据的方法,通过contenttype值来找对应方法解析,如果有一天你写了一个contenttype类型,定义了一个消息格式,各大语言及框架都支持,那么别人也会写一个针对你的contenttype值来解析数据的方法,django里面不能帮我们解析contenttype值为json的数据格式,你知道他能帮你解析application/x-www-form-urlencoded 和multipart/form-data(文件上传会用到)就行了,如果我们传json类型的话,需要我们自己来写一个解析数据的方法,其实不管是什么类型,我们都可以通过原始发送来的数据来进行加工处理,解析出自己想要的数据,这个事情我们在前面自己写web框架的时候在获取路径那里就玩过了,还记得吗?
$.ajax({
url:"{% url 'home' %}",
type:'post',
headers:{
"X-CSRFToken":$.cookie('csrftoken'), #现在先记住,等学了cookies你就明白了
contentType:'json',
}, data:JSON.stringify({ //如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,必须通过csrf的方式3的形式来写,写在hearders(请求头,可以写一些自定制的请求头)里面,注意,其实contentType也是headers里面的一部分,写在里面外面都可以
name:name,
//csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
}),
success:function (response) { } })
基于form表单的文件上传
模板部分
<form action="" method="post" enctype="multipart/form-data"> #上面说的其他两种contenttype都是键值的形式发送数据,这种form_data的格式一般是把大数据一段一段隔开的
用户名 <input type="text" name="user">
头像 <input type="file" name="avatar"> #如果不用form_data格式来发,那么默认的是urlencoded的格式,这个标签的数据会组成avatar:文件名字来进行发送
<input type="submit">
</form>
视图部分
def index(request):
print(request.body) # 原始的请求体数据
print(request.GET) # GET请求数据
print(request.POST) # POST请求数据
print(request.FILES) # 上传的文件数据 return render(request,"index.html")
upload.py,内容如下:
def upload(request): if request.method == 'GET': return render(request,'upload.html')
else:
print(request.POST)
username = request.POST.get('user')
file_obj = request.FILES.get('file_obj') #获得文件数据对象
print('>>>',file_obj,type(file_obj))
#>>> jaden博客.txt <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>,一个文件对象,可以理解为一个文件句柄
file_name = file_obj.name #jaden博客.txt
print(file_name)
# 将数据写到文件里面,需要名字,需要数据
with open(file_name,'wb') as f: #直接把文件名字放这里,那么文件将直接生成在django的整个项目目录下,因为django配置的系统搜索的根路径就是咱们的项目文件夹路径,那个BASE_DIR,一般我们需要自己建立一个文件夹专门存放上传的文件
#所以需要我们自己来拼接一个路径放到这里,os.path.join(settings.BASE_DIR,'media','img',file_name)
# f.write() #不能一下写进去,占用的内容太多,要一点一点写
for data in file_obj: #读数据
f.write(data) #每次读取的data不是固定长度的,和读取其他文件一样,每次读一行,识别符为\r \n \r\n,遇到这几个符号就算是读了一行
#for chunks in file_obj.chunks(): #chunks()默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器
# f.write(chunks)
通过js来找文件对象
基于Ajax的文件上传
模板
<form> #用不用form没关系,这里就是个盒子的作用,一般写form标签是为了提示别人,这个地方的内容是要提交的
{% csrf_token %}
用户名 <input type="text" id="user">
头像 <input type="file" id="avatar">
<input type="button" id="ajax-submit" value="ajax-submit">
</form> <script> $("#ajax-submit").click(function(){
var formdata=new FormData(); #ajax上传文件的时候,需要这个类型,它会将添加给它的键值对加工成formdata的类型
formdata.append("user",$("#user").val()); #添加键值的方法是append,注意写法,键和值之间是逗号
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); #别忘了csrf_token
formdata.append("avatar_img",$("#avatar")[0].files[0]);
$.ajax({ url:"",
type:"post",
data:formdata, #将添加好数据的formdata放到data这里
processData: false , // 不处理数据
contentType: false, // 不设置内容类型 success:function(data){
console.log(data)
}
}) }) </script>
或者使用
var form = document.getElementById("form1");
var fd = new FormData(form);
这样也可以直接通过ajax 的 send() 方法将 fd 发送到后台。
注意:由于 FormData 是 XMLHttpRequest Level 2 新增的接口,现在 低于IE10 的IE浏览器不支持 FormData。
视图
def index(request): if request.is_ajax(): print(request.body) # 原始的请求体数据
print(request.GET) # GET请求数据
print(request.POST) # POST请求数据
print(request.FILES) # 上传的文件数据 return HttpResponse("ok") return render(request,"index.html")
检查浏览器的请求头:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaWl9k5ZMiTAzx3FT
关于django后端代码接受上传文件的方法
练习(用户名是否已被注册)
功能介绍
在注册表单中,当用户填写了用户名后,把光标移开后,会自动向服务器发送异步请求。服务器返回这个用户名是否已经被注册过。
案例分析
- 页面中给出注册表单;
- 在username input标签中绑定onblur事件处理函数。
- 当input标签失去焦点后获取 username表单字段的值,向服务端发送AJAX请求;
- django的视图函数中处理该请求,获取username值,判断该用户在数据库中是否被注册,如果被注册了就返回“该用户已被注册”,否则响应“该用户名可以注册”。
九、关于Json
1.什么是json
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言 *
- JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
啥都别多说了,上图吧!
json数据类型和python数据类型的对比:
object和python的dict类型是差不多的,但是要求里面必须是双引号,string和list、tuple等也是一样的,都是双引号。python中的datetime等时间日期类型是不能进行json序列化的,因为json没有对应的格式,上面的这几种数据类型虽然进行json.dumps序列化之后都是个字符串,但是也是有格式的
前端ajax拿到后端返回的一个python的json模块序列化之后的一个json字符串,那么js通过自己的json接口,将接受到的json字符串来反序列化为js自己语言能够识别的数据类型,然后再进行操作。
相当于我有一个json方法,你有一个json方法,你给我发数据必须是json字符串的格式,那么你就需要将你的数据类型序列化为json的字符串,那么序列化的时候,就把你的数据序列化为了符合json标准的字符串,然后我接收到这个字符串之后,我通过我的json方法,将数据转换为我的语言支持的数据类型。在进行反序列化的时候,如果你的字符串不符合json的格式,那么反序列化的时候就会报错,所以只要你是通过json序列化成的字符串,都是能够json反序列化的,因为json序列化的时候,就把你的数据改为了符合json标准的字符串形式,例如:里面的单引号,序列化后变成了双引号。
合格的json对象:
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 } #这就是一个json的object类型,符合json的标准格式,就可以通过dumps来进行序列化
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]
不合格的json对象:
{ name: "张三", 'age': 32 } // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用undefined
{ "name": "张三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function() {return this.name;} // 不能使用函数和日期对象
}
看一下普通字符串和json字符串,在进行序列化的时候的区别
import json
# s = "{'name':'chao','age':18}" #普通字符串,每加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。
s = '{"name":"chao","age":18}' #json字符串,里面必须是双引号
ret = json.loads(s)
print(ret)
print(ret['name'])
2.js的stringify与parse方法
JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象
JSON.parse('{"name":"chao"}');
JSON.parse('{name:"chao"}') ; // 错误
JSON.parse('[18,undefined]') ; // 错误
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。
JSON.stringify({"name":"chao"})
3.和XML的比较
JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁琐笨重的 XML 格式。
JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。
XML和JSON都使用结构化方法来标记数据,下面来做一个简单的比较。
用XML表示中国部分省市数据如下:
用JSON表示如下:
由上面的两端代码可以看出,JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽。
4.ajax和服务端的数据交互时的序列化问题
当我们给ajax回复的不是一个字符串,而是其他数据类型的时候,需要我们将数据转换为json字符串进行发送,这样好配合js进行json字符串的处理,不然发送或者接受的是普通字符串的话,没办法处理成原来的数据类型。
这就用到了我们前面的视图函数中那个JsonResponse了,看博客,里面response的部分
还要注意ajax中的data参数:
data参数中的键值对,如果值值不为字符串,需要将其转换成字符串类型。
$("#b1").on("click", function () {
$.ajax({
url:"/ajax_add/",
type:"GET",
data:{"i1":$("#i1").val(),"i2":$("#i2").val(),"hehe": JSON.stringify([1, 2, 3])},
success:function (data) {
$("#i3").val(data);
}
})
})
5.Django内置的serializers做序列化
看代码:
def books_json(request):
book_list = models.Book.objects.all()[0:10]
from django.core import serializers
ret = serializers.serialize("json", book_list)
return HttpResponse(ret)
通过json学列化时间日期格式数据的时候需要注意,不能直接序列化,我写了一个类,可以借用:
import json
from datetime import datetime
from datetime import date #对含有日期格式数据的json数据进行转换
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field,datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field,date):
return field.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder)
print(dd)