Django与Ajax
一 、什么是Ajax
1.1 简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
1. 2.示例
页面输入两个整数,通过AJAX传输到后端计算出结果并返回。
html文件名称为ajax_demo1.html,内容如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>AJAX局部刷新实例</title> </head> <body> <input type="text" id="i1">+ <input type="text" id="i2">= <input type="text" id="i3"> <input type="button" value="AJAX提交" id="b1"> <script src="/static/jquery-3.2.1.min.js"></script> <script> $("#b1").on("click", function () { $.ajax({ url:"/ajax_add/", //别忘了加双引号 type:"GET", data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, //object类型,键值形式的,可以不给键加引号 success:function (data) { $("#i3").val(data); } }) }) </script> </body> </html>
views.py里面的内容:
def ajax_demo1(request): return render(request, "ajax_demo1.html") def ajax_add(request): #time.sleep(10) #不影响页面发送其他的请求 i1 = int(request.GET.get("i1")) i2 = int(request.GET.get("i2")) ret = i1 + i2 return JsonResponse(ret, safe=False)
urls.py里面的内容
urlpatterns = [ ... url(r'^ajax_add/', views.ajax_add), url(r'^ajax_demo1/', views.ajax_demo1), ... ]
1. 3.AJAX常见应用情景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
a.整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
b.当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
1.4.AJAX的优缺点
优点:
1.AJAX使用JavaScript技术向服务器发送异步请求;
2.AJAX请求无须刷新整个页面;
3.因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
二、基于Ajax进行登录验证
写一个登陆认证页面,登陆失败不刷新页面,提示用户登陆失败,登陆成功自动跳转到网站首页。
login.html,内容如下:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> 用户名:<input type="text" id="username"> 密码:<input type="text" id="pwd"> {% csrf_token %} <button id="sub">提交</button> <span style="color: red;font-size: 12px;" id="error"></span> </div> <script src="{% static 'jquery.js' %}"></script> <script> $('#sub').click(function () { $.ajax({ url:"{% url 'login' %}", type:'post', data:{username:$('#username').val(),pwd:$('#pwd').val(),csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val()}, success:function (data) { data = JSON.parse(data); console.log(data,typeof data); if (data['status']){ location.href=data['home_url']; } else { $('#error').text('用户名或者密码错误!') } } }) }) </script> </body> </html>
base.html,内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1> 欢迎来到xxx官网 </h1> </body> </html>
urls.py,内容如下
url(r'^login/', views.login,name='login'),
url(r'^home/', views.home,name='home'),
views.py,内容如下
def login(request): res_dict = {'status':None,'home_url':None} if request.method == 'GET': return render(request,'login.html') else: uname = request.POST.get('username') pwd = request.POST.get('pwd') user_obj = models.UserInfo.objects.filter(name=uname,password=pwd).exists() import json if user_obj: res_dict['status'] = True res_dict['home_url'] = reverse('home') res_json_dict = json.dumps(res_dict) return HttpResponse(res_json_dict) #直接回复字典格式是不可以的,必须转换成json字符串,转换成普通字符串也是不行的,因为前端需要对json进行反序列获得这个字典,在通过字典的形式来操作数据。 else: res_dict['status'] = False res_json_dict = json.dumps(res_dict) return HttpResponse(res_json_dict) # 如果你就是不使用JsonResponse的话,也可以给HttpResponse添加一个参数,content_type='application/json',那么前端ajax拿到数据之后,也是不需要反序列化的,ajax的回调函数就收到的就是一个反序列化之后的一个对象,因为ajax接受到数据后,通过这个data_type或者content_type发现你发送来的是个json格式的数据,那么ajax内容就自动将这个数据反序列化得到了js的数据对象,然后通过对象可以直接操作数据。
# return HttpResponse(res_json_dict,data_type='application/json')
# return JsonResponse(res_dict) def home(request): return render(request,'base.html')
三 Ajax的使用
3. 1.基于jQuery的实现
<button class="send_Ajax">send_Ajax</button> <script> $(".send_Ajax").click(function(){ $.ajax({ url:"/handle_Ajax/", type:"POST", data:{username:"chao",password:123}, success:function(data){ console.log(data) }, error: function (jqXHR, textStatus, err) { console.log(arguments); }, complete: function (jqXHR, textStatus) { console.log(textStatus); }, statusCode: { '403': function (jqXHR, textStatus, err) { console.log(arguments); }, '400': function (jqXHR, textStatus, err) { console.log(arguments); } } }) }) </script>
3.2 Ajax-服务器-Ajax流程图
3. 3.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会对数据进行深层次迭代;
四 Ajax请求设置csrf_token
方式1
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送,一般都放在form表单中写上{% csrf_token %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="text" name="password" id="pwd"></p>
<p><input type="submit"></p>
</form>
<button class="btn">点我</button>
</body>
<script>
$(".btn").click(function () {
$.ajax({
url: '',
type: 'post',
data: {
'name': $('[name="name"]').val(),
'password': $("#pwd").val(),
'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
},
success: function (data) {
console.log(data)
}
})
})
</script>
</html>
方式2
使用$.ajaxSetup()方法为ajax请求统一设置。
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式3
通过获取返回的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就可以这样加 })
跨站请求伪造CSRF
详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息
所以解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。
五、 Ajax文件上传
5.1 请求头ContentType
ContentType指的是请求体的编码类型,常见的类型共有3种:
1 application/x-www-form-urlencoded
浏览器默认的请求体格式,不管是post请求还是get请求,携带的数据都是这样拼接数据 user=yuan&age=22,django能够解析这个格式的数据
但是当我们指定了请求数据格式为application/json,django解析不了,过程看标题六
如果不设置 enctype
属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
表单代码:
<form action="http://localhost:8888/task/" method="POST"> First name: <input type="text" name="firstName" value="Mickey&"><br> Last name: <input type="text" name="lastName" value="Mouse "><br> <input type="submit" value="提交"> </form>
通过测试发现可以正常访问接口,在Chrome的开发者工具中可以看出,表单上传编码格式为application/x-www-form-urlencoded
(Request Headers中)
我们可以看出,服务器知道参数用符号&
间隔,如果参数值中需要&
,则必须对其进行编码。编码格式就是application/x-www-form-urlencoded
(
将键值对的参数用&连接起来,如果有空格,将空格转换为+
加号;有特殊符号,将特殊符号转换为ASCII HEX
值)。
application/x-www-form-urlencoded
是浏览器默认的编码格式。对于Get请求,是将参数转换?key=value&key=value
格式,连接到url后
2 multipart/form-data
那么当服务器使用multipart/form-data
接收POST请求时,服务器怎么知道每个参数的开始位置和结束位置呢?
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 表单的 enctype
等于 multipart/form-data。form表单不支持发json类型的contenttype格式的数据,
而ajax什么格式都可以发,也是ajax应用广泛的一个原因。
<form action="http://localhost:8888/task/" method="POST" enctype="multipart/form-data"> First name: <input type="text" name="firstName" value="Mickey&"><br> Last name: <input type="text" name="lastName" value="Mouse "><br> <input type="submit" value="提交"> </form>
我们在开发者工具中可以看出multipart/form-data
不会对参数编码,使用的boundary
(分割线),相当于&
,boundary
的值是----Web**AJv3
。
3. application/json
application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串
如果在ajax里面写上这个contenttype类型,那么data参数对应的数据,就不能是个object类型数据了,必须是json字符串,contenttype:'json',简写一个json,它也能识别是application/json类型
务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据,其实就是服务端框架已经写好了针对这几个类型的不同的解析数据的方法
5.2 基于Form表单上传文件
<form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} 用户名: <input type="text" name="username"> 密码: <input type="password" name="password"> {# 头像: <input type="file" name="avatar" multiple>#} 头像: <input type="file" name="avatar"> <input type="submit"> </form>
必须指定 enctype=”multipart/form-data”
视图函数:
def upload(request): if request.method == 'GET': return render(request, 'upload.html') else: print(request.POST) #如果没有修改请求消息格式(content-type),那么文件名称会在request.POST中拿到,但是只是文件名称 print(request.FILES) # 里面放的是文件数据(文件对象,类似于文件句柄) # print(request.FILES.getlist('avatar')) # print(request.FILES.get('avatar')) # timg.jpg file_obj = request.FILES.get('avatar') print(file_obj.name) #文件名称 with open(file_obj.name, 'wb') as f: # for i in file_obj: # f.write(i) for i in file_obj.chunks(): #默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器 f.write(i) return HttpResponse('ok')
5.3 基于Ajax的文件上传
通过js来找文件对象
前端部分:
$("#sub").click(function (){ var uname = $('[name="username"]').val(); var pwd = $('[name="password"]').val(); var file_obj = $('[type="file"]')[0].files[0]; // ajax上文件,必须将文件数据放到一个叫做formdata的对象中才能发送 var formdata = new FormData(); formdata.append('username',uname) formdata.append('password',pwd) formdata.append('csrfmiddlewaretoken', '{{ csrf_token }}') formdata.append('avatar',file_obj) // requser.FILES.get('avatar') $.ajax({ url:'/upload/', type:'post', data:formdata, // ajax会将本次请求的数据格式改为 content-type: multipart/form-data // 告诉ajax不要对数据进行预处理和加工,向上传文件必须加上这两个参数 contentType:false, processData: false, success:function (res){ } }) });
view.py视图函数和form一样
def upload(request): if request.method == 'GET': return render(request, 'upload.html') else: print(request.POST) #如果没有修改请求消息格式(content-type),那么文件名称会在request.POST中拿到,但是只是文件名称 print(request.FILES) # 里面放的是文件数据(文件对象,类似于文件句柄) # print(request.FILES.getlist('avatar')) # print(request.FILES.get('avatar')) # timg.jpg file_obj = request.FILES.get('avatar') print(file_obj.name) #文件名称 with open(file_obj.name, 'wb') as f: # for i in file_obj: # f.write(i) for i in file_obj.chunks(): #默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器 f.write(i) return HttpResponse('ok')
六 Ajax提交json格式数据
$("#ajax_test").click(function () { var dic={'name':'lqz','age':18} $.ajax({ url:'', type:'post', contentType:'application/json', //一定要指定格式 contentType: 'application/json;charset=utf-8', data:JSON.stringify(dic), //转换成json字符串格式 success:function (data) { console.log(data) } }) })
#django的request.POST拿不到数据了,需要我们自己去request.body中获取原始的bytes类型数据,然后进行解析 data = request.body data = data.decode() import json data = json.loads(data) print(data,type(data))