【Day41】Python之路——AJAX
什么是AJAX
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
优点:
不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容
不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行
同源策略与jsonp
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
jsonp(jsonpadding)
之前发ajax的时候都是在自己给自己的当前的项目下发
现在我们来实现跨域发。
ajax请求的本质:生成xmlHttpRequest对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="zh-CN"> <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>index</title> <script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script> </head> <body> <form action="" method="" > <input type="text" name="k1" value="v1"> <input type="text" name="k2" value="v2"> <input id="forms_btn" type="button" value="提交"> {% csrf_token %} </form> <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> </body> </html>
下面是,前端以json格式 发往后端的JavaScript代码
<script>
$("#forms_btn").click(function () {
$.ajax({
url:"/fromData/",
type:"post",
dataType:"json", // 注意 :是预期服务器返回的数据类型
headers:{"X-CSRFToken":$("[name='csrfmiddlewaretoken']").val()},
contentType:"application/json",
data:{
"name":$("[name='k1']").val(),
"name":$("[name='k2']").val(),
},
})
})
</script>
注意:
1、设置contentType:默认编码格式 “url_encoding” ?a=1&b=2
2、设置headers
3、data, 在后端接受到是以二进制形式(b'password=v1&name=v2'),需要反序列化
前端的JavaScript data{“a”:1,“b”:2}, 其中a,b 可以不用提前定义,就使用,所以可以这样使用:data{a:1,b:2}和Python不同
4、后台取数据不能在POST中取,需要在body中取
dataType
前端设置dataType ,后端如果返回的非json数据,后端不能识别,但是不报错,
使用Ajax跨域
view层
def ajax_send(request): func=request.GET.get("callback") print("func",func) res={"name":"alex"} import json return HttpResponse("%s('%s')"%(func,json.dumps(res)))
ajax 无法跨域访问其他网站,
<script> $(".b1").click(function () { $.ajax({ url:"http://127.0.0.1:8002/ajax_send/", // 浏览器的同源策略的原因,AJax无法发送跨域请求 success:function (data) { alert(data) } }) }); </script>
下面看如何解决跨域访问
跨域访问初级版
<script> function foo(s) { console.log(s); JSON.parse(s) } function kua_yu(url) { // 生成 script标签 var $ele_script=$("<script>"); 注意:使用了 $ele_script创建标签 $ele_script.attr("src",url); $ele_script.attr("class","kuayu"); // 添加到body中 $("body").append($ele_script); // 发送请求 $(".kuayu").remove() // 生成之后即删除,不会产生多余的标签 } $(".b2").click(function () { kua_yu( "http://127.0.0.1:8002/ajax_send/?callback=foo") }); </script>
跨域访问进阶版
$(".b1").click(function () {
$.getJSON("http://127.0.0.1:8002/ajax_send/?callback=?",function (data) { // function 使用了回调函数,
console.log(data);
})
});
要注意的是在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个?是内部自动生成的一个回调函数名
跨域访问高级版
<script> $(".b1").click(function () { $.ajax({ url:'http://127.0.0.1:8002/ajax_send/', dataType:"jsonp", jsonp: 'callback', jsonpCallback:"SayHi" }); }); $(".b2").click(function () { $.ajax({ url:'http://www.jxntv.cn/data/jmd-jxtv2.html?', dataType:"jsonp", jsonp: 'callback', jsonCallback:"list" // 访问的函数名,可定制 }); }); function list(data) { console.log(data.data); $.each(data.data,function (i,weekday) { //console.log(j); // {week: "周日", list: Array(19)} $("body").append("<p>"+weekday.week+"</p>"); console.log(weekday.list); $.each(weekday.list,function (j,show) { s="<p><a href='"+show.link+"'>"+show.name+"</a></p>" $("body").append(s); }) }) } </script>
注意:jsonp 一定是GET请求
csrf跨站请求伪造
如果把type:"GET" 改为type:"POST" 会报一个Forbidden的错
解决办法有三种:
方式一:
$.ajaxSetup({ data:{csrfmiddlewaretoken:'{{ csrf_token }}'} }); 注意:要放在ajax请求的前面,在发送之前组装一组字符串,在第一步render的时候就发了 所以有局限性: 如果把JS代码放到静态文件中,不会渲染,不会执行{{csrf_token}},只能在HTML页面中使用
方式二:自己组装一组键值对 ( 推荐)
<form>
{% csrf_token %}
</form> data:{ csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), name:$(":text").val(), pwd:$(":password").val() },
方式三:自己设置头信息
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
$.ajax({ url:"/serialize/", type:"POST", headers:{"X-CSRFToken":$.cookie('csrftoken')}, })
jQuery.serialize()
serialize()
函数用于序列化一组表单元素,将表单内容编码为用于提交的字符串。
serialize()
函数常用于将表单内容序列化,以便用于AJAX提交。
该函数主要根据用于提交的有效表单控件的name和value,将它们拼接为一个可直接用于表单提交的文本字符串,该字符串已经过标准的URL编码处理(字符集编码为UTF-8)。
该函数不会序列化不需要提交的表单控件,这和常规的表单提交行为是一致的。例如:不在<form>标签内的表单控件不会被提交、没有name属性的表单控件不会被提交、带有disabled属性的表单控件不会被提交、没有被选中的表单控件不会被提交。
与常规表单提交不一样的是:常规表单一般会提交带有name的按钮控件,而serialize()函数不会序列化带有name的按钮控件。更多详情请点击这里。
语法:
jQueryObject.serialize( )
serialize()
函数的返回值为String类型,返回将表单元素编码后的可用于表单提交的文本字符串。
请参考下面这段初始HTML代码:
<form name="myForm" action="http://www.365mini.com" method="post"> <input name="uid" type="hidden" value="1" /> <input name="username" type="text" value="张三" /> <input name="password" type="text" value="123456" /> <select name="grade" id="grade"> <option value="1">一年级</option> <option value="2">二年级</option> <option value="3" selected="selected">三年级</option> <option value="4">四年级</option> <option value="5">五年级</option> <option value="6">六年级</option> </select> <input name="sex" type="radio" checked="checked" value="1" />男 <input name="sex" type="radio" value="0" />女 <input name="hobby" type="checkbox" checked="checked" value="1" />游泳 <input name="hobby" type="checkbox" checked="checked" value="2" />跑步 <input name="hobby" type="checkbox" value="3" />羽毛球 <input name="btn" id="btn" type="button" value="点击" /> </form>
对<form>元素进行序列化可以直接序列化其内部的所有表单元素。
// 序列化<form>内的所有表单元素 // 序列化后的结果:uid=1&username=%E5%BC%A0%E4%B8%89&password=123456&grade=3&sex=1&hobby=1&hobby=2 alert( $("form").serialize() );
我们也可以直接对部分表单元素进行序列化。
// 序列化所有的text、select、checkbox表单元素 // 序列化后的结果:username=%E5%BC%A0%E4%B8%89&password=123456&grade=3&hobby=1&hobby=2 alert( $(":text, select, :checkbox").serialize() );
示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> <title>Title</title> </head> <body> <form name="myForm" action="http://www.365mini.com" method="post"> <input name="uid" type="hidden" value="1" /> <input name="username" type="text" value="张三" /> <input name="password" type="text" value="123456" /> <select name="grade" id="grade"> <option value="1">一年级</option> <option value="2">二年级</option> <option value="3" selected="selected">三年级</option> <option value="4">四年级</option> <option value="5">五年级</option> <option value="6">六年级</option> </select> <input name="sex" type="radio" checked="checked" value="1" />男 <input name="sex" type="radio" value="0" />女 <input name="hobby" type="checkbox" checked="checked" value="1" />游泳 <input name="hobby" type="checkbox" checked="checked" value="2" />跑步 <input name="hobby" type="checkbox" value="3" />羽毛球 <input name="btn" id="btn" type="button" value="点击" /> </form> <script src="/static/jquery-3.2.1.min.js"></script> <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> <script> $("#btn").click(function () { {# 方式一#} //$.ajaxSetup({ // data:{csrfmiddlewaretoken:'{{ csrf_token }}'} //}); $.ajax({ url:"/serialize/", type:"POST", {# 方式三#} headers:{"X-CSRFToken":$.cookie('csrftoken')}, //data:$("form").serialize(), //序列form表单所有的 data:$(":text,:password,:checkbox").serialize(), //序列自己选择的 success:function (data) { var data=JSON.parse(data); //js中的反序列化 console.log(data); console.log(typeof data); $(".error").html(data); } }) }) </script> </body> </html> serialize.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def serialize(request): 2 # form = request.POST 3 # print(form) 4 name = request.POST.get("username") 5 password = request.POST.get("password") 6 checked = request.POST.getlist("hobby") 7 print(name,password,checked) 8 return HttpResponse(json.dumps(name))
当有好多input的时候,就得一一对应的吧所有的数据发过去的,这样显得麻烦,我们用序列化 jQuery.serialize() data:$("form").serialize(), //序列form表单所有的 data:$(":text,:password,:checkbox").serialize(), //序列自己选择的 在服务端获取数据 form = request.POST print(form) #获取所有 name = request.POST.get("username") password = request.POST.get("password") checked = request.POST.getlist("hobby") print(name,password,checked)#获取单个
Ajax上传文件(利用FormData)
既可以处理二进制,又可以处理字典,列表啊等
FormData是什么呢?
FormData
.利用FormData对象
,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()
方法来异步的提交这个"表单".比起普通的ajax,使用FormData
的最大优点就是我们可以异步上传一个二进制文件.$("#upload") 拿到的是一个集合 $("#upload")[0] 就是一个dom对象 $("#upload")[0].files 拿到的是一个filelist $("#upload")[0].files[0] 拿到的是当前最近的文件对象
要是使用FormData一定要加上:
一定要加上:
contentType:false
processDate:false #不做预处理
ajaxupload.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"> <title>Title</title> </head> <body> <p>姓名<input type="text" name="username"></p> <p>头像<input type="file" id="upload"></p> <p><button class="btnnn">提交</button><span class="tishi"></span></p> <script src="/static/jquery-3.2.1.min.js"></script> <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> <script> $(".btnnn").click(function () { var formData=new FormData(); formData.append("username",$(":text").val()); formData.append("file",$("#upload")[0].files[0]); $.ajax({ url:"/get_upload/", type:"POST", headers:{"X-CSRFToken":$.cookie('csrftoken')}, data:formData, contentType:false, processData:false, success:function (data) { $(".tishi").html("上传成功") } }) }) </script> </body> </html>
view.py
def ajaxupload(request): return render(request,"ajaxupload.html") def get_upload(request): if request.method == "POST": print("FIFLE", request.FILES) file_obj = request.FILES.get("file") print(file_obj.name, "-----") file_obj = request.FILES.get("file") with open(file_obj.name, "wb") as f: for i in file_obj: f.write(i) return HttpResponse("上传成功")