Ajax发送POST请求
Ajax发送POST请求
一、 Ajax 介绍
Ajax 就是可以让页不刷新的情况下,给服务器发送 HTTP 请求并获取到数据。
通过编写 JavaScript 代码,还可以把获取到的数据展示在当前页面上。
二、 jQuery 封装的 Ajax
语法格式
$.ajax({
/* url 地址可以是 /get-json/ 的方式
* 也可以是 http://www.qfedu.com/get-json/ 的方式
*/
url: 'url 地址',
type: 'GET/POST',
dataType: 'json',
success: function(res){
// 成功处理逻辑
},
error: function(res){
// 错误时处理逻辑
}
});
image.png
Django POST 方法提交表单,必须面临一个问题,那就是 CSRF_TOKEN
CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
Django 中自带了这个防止CSRF攻击的功能。
GET 请求不需要 CSRF 认证,POST 请求需要正确认证才能得到正确的返回结果。
解决的思路是:
- 在前端代码的 form 表单内写上 {% csrf_token %},
用来接收 Django 返回的 csrf_token 的值
{% csrf_token %} 会在表单中生一个隐藏的 input 标签
<input type="hidden" name="csrfmiddlewaretoken" value="EAYZaWHO8g7ZtI4N2xPzKCvxj94sCNPnsYTCLQpQWudH38iK0vHoyYf8NtRBjcRP">
\2. 想办法把此标签的 key 和 value 作为首层数据发送到后端,Django 后端会自动获取验证
获取到 csrf_token 的 key 和 value 的方法有一下几种
- 从页面中的源码中获得。
当第一次请求后不论验证结果如何,前端页面在写上 {% csrf_token %}
的前提下,就会有个隐藏的 input 标签,复制他的 name 属性的值就是 key, 复制 value属性的值就是我们要的 value - 从cookie 中得到。
利用 javascript.cookie.js 可以从 cookie 中得到。
-
javascript.cookie.js 下载地址
就在其源码的 src 目录下 。使用方法:
// 引入 <script src="/static/jquery.cookie.js"></script> <script> // 获取到值 var token_val = $.cookie('csrftoken') </script>
- 从 form 表单中自动获取
<form >
{% csrf_token %}
<input type="file" />
<input type="text" name="one" value="FormData_one"/>
<input type="text" name="two" value="FormData_two"/>
<button >提交</button>
</form>
<script>
$("#Ajax_FD_submit").serialize());
</script>
- serialize() 会获取到在 form 标签内的所有数据,并进行序列化;这种适合提交数据比较多的情况
- 利用 Django 的模版语言
{{ csrf_token }} // 它就是 csrf_token 的值
- 注意一点: csrf_tken 的数据向后台提交的时候,必须放在首层,不要在嵌套到其他数据类中了
因为 Django 会从 GET 或 POST 的数据体内寻找固定的 csrf_token key 的值来验证。
下面来说怎么结合以上的方法,具体实现 Ajax 提交数据并进行 csrf_token 认证
第一种: 结合 JavaScript 的 FormDate 类,实现 Ajax 提交
简单介绍
- 实现的功能:可以提交表单中的数据和文件。
- 缺点:目前兼容性还不太好,因为 FormDate 这个类是比较新的特性,IE8 及一下版本的浏览器不支持。
- 相信随着时间的推移,这种方法会逐渐称为主流,必须掌握。
FormData
FormData 是 JavaScript 的一个新特性,他可以让你对其进行实例化出一个对象,这个对象就像是一个盒子、容器,你可以往里面填充数据,填充的方法就像是 Python 中的列表一样。目前我所知道的是每次只可以添加一对,这对儿数据就是 'key','value' 的形式。
实现
前端Ajax发送数据的代码:
<h1>Ajax上传文件</h1>
<form >
{% csrf_token %}
<input type="file" />
<input type="text" name="one" value="FormData_one"/>
<input type="text" name="two" value="FormData_two"/>
<button >Ajax提交</button>
</form>
<script>
$(function () {
$('#ajax_btn').click(function () {
var fmd_obj = new FormData(); // 实例化一个对象
// 向对象中添加数据,格式:fmd_obj.append('key','value')
fmd_obj.append('sub_file_name', document.getElementById('sub_file').files[0]);
fmd_obj.append('my_list', ['a','b']); //每次都可以向这个对象中添加一个数据
fmd_obj.append('my_key','my_value');
fmd_obj.append('csrfmiddlewaretoken','{{ csrf_token }}') // 解决认证问题
fmd_obj.append('data',$("#Ajax_FD_submit").serialize());
$.ajax({
url: "/ajax-upload/",
type: 'POST',
data: fmd_obj,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success:function (arg) {
console.log(arg);
},
});
return false; // 最好返回false,因为如果按钮类型是submit,或者是 button 标签,
// 则表单自己又会以 GET 的方式再提交一次;返回false阻止表单再次提交
});
});
</script>
后端接收数据的代码
def ajax_upload(request):
"""
接收前端 Ajax 发送过来的数据和文件
:param request:
:return:
"""
import os,json
response = BaseReponse()
try:
print('reqpost数据>>:', request.POST)
print('reqfile数据>>:', request.FILES)
file_obj = request.FILES.get('sub_file_name')
print('接收到的文件>>:', file_obj)
print('参数key one 的值>>:', request.POST.get('one'))
print('参数key two 的值>>:', request.POST.get('two'))
# file_dir = os.path.join(settings.BASE_DIR, 'img') # 设置接收文件的绝对路径
# print("文件保存的绝对路径>>:", file_dir)
"""下面的是把接收到的文件保存到服务器上"""
file_path = os.path.join('static', file_obj.name) # 组合文件的完整路径
new_file_obj = open(file_path, 'wb')
[new_file_obj.write(chunk) for chunk in file_obj.chunks()]
new_file_obj.close()
"""设置一下返回信息和状态"""
response.status = True
response.data = file_path
except Exception as e:
response.status = False
response.error = "上传失败"
return HttpResponse(json.dumps(response.__dict__))
后端得到的数据
reqpost数据>>: <QueryDict: {'my_list': ['a,b'], 'csrfmiddlewaretoken': ['EAYZaWHO8g7ZtI4N2xPzKCvxj94sCNPnsYTCLQpQWudH38iK0vHoyYf8NtRBjcRP'], 'my_key': ['my_value'], 'data': ['csrfmiddlewaretoken=EAYZaWHO8g7ZtI4N2xPzKCvxj94sCNPnsYTCLQpQWudH38iK0vHoyYf8NtRBjcRP&one=FormData_one&two=FormData_two']}>
reqfile数据>>: <MultiValueDict: {'sub_file': [<InMemoryUploadedFile: 乖巧中透漏这悲伤.png (image/png)>]}>
接收到的文件>>: 乖巧中透漏这悲伤.png
参数key one 的值>>: None
参数key two 的值>>: None
- 因为 .serialize() 得到的是 form 表单内的所有数据,所以在内层数据中也会有个 csrf_token
第二种: 利用 Form 和 ifram 实现"伪"Ajax 提交
简单介绍
- 实现的功能:可以提交表单和文件;目前业界比较流行
关键点
form 标签要定义一个 target 属性,其值必须与 ifram 标签的 name 属性的值相同
实现
前端"伪"Ajax发送数据的代码:
<h1>"伪"Ajax操作</h1>
//可以同时提交文件和数据
<div>
<form action="/ajax-upload/" method="POST" enctype="multipart/form-data" target="ifr">
{% csrf_token %}
<input type="file" name="sub_file_name" />
<input type="text" name="one" value="iframe_one"/>
<input type="text" name="two" value="iframe_two"/>
</form>
<iframe name="ifr"style="display: none"></iframe>
// 用于展示预览的图片
<div ></div>
</div>
<script>
// "伪"Ajax操作,自动上传
// 标签的内容有改变,就触发 change 事件
function changeImg() {
document.getElementById('ifram_form').submit(); // js 语言的提交
// $("#ifram_form").submit(); // jQuery 方法提交
}
;
// 当标签内容(包括图片和文字)加载完毕时触发 load 事件
$("#onload").load(function () {
var v = $('#onload').contents().find('body').html(); // 返回的数据会在 ifram 中,从ifram中取数据
var obj = JSON.parse(v); // JavaScript 的反序列化,把字符串转换为数据对象
alert(obj);
var tag = document.createElement('img'); // 创建一个 img 标签
tag.src = "/" + obj.data; // 给标签添加一个属性,并赋值
$('#prevImg').append(tag); // 在某一个标签的里面,添加一个这个标签的子标签
});
</script>
后端接收数据的代码
def ajax_upload(request):
"""
接收前端 Ajax 发送过来的数据和文件
:param request:
:return:
"""
import os,json
response = BaseReponse()
try:
print('reqpost数据>>:', request.POST)
print('reqfile数据>>:', request.FILES)
file_obj = request.FILES.get('sub_file_name')
print('接收到的文件>>:', file_obj)
print('参数key one 的值>>:', request.POST.get('one'))
print('参数key two 的值>>:', request.POST.get('two'))
# file_dir = os.path.join(settings.BASE_DIR, 'img') # 设置接收文件的绝对路径
# print("文件保存的绝对路径>>:", file_dir)
"""下面的是把接收到的文件保存到服务器上"""
file_path = os.path.join('static', file_obj.name) # 组合文件的完整路径
new_file_obj = open(file_path, 'wb')
[new_file_obj.write(chunk) for chunk in file_obj.chunks()]
new_file_obj.close()
"""设置一下返回信息和状态"""
response.status = True
response.data = file_path
except Exception as e:
response.status = False
response.error = "上传失败"
return HttpResponse(json.dumps(response.__dict__))
后端得到的数据
reqpost数据>>: <QueryDict: {'one': ['iframe_one'], 'csrfmiddlewaretoken': ['rASMcicdXWgDqO83szbzkVQtY5UOiKpAfYNpNcUfLaml0em0qx3o8hA4spHXZ9r2'], 'two': ['iframe_two']}>
reqfile数据>>: <MultiValueDict: {'sub_file': [<InMemoryUploadedFile: QQ截图20170305224609.png (image/png)>]}>
接收到的文件>>: QQ截图20170305224609.png
参数key one 的值>>: iframe_one
参数key two 的值>>: iframe_two
第三种: 利用 javascript.foram.js 实现 Ajax 提交
简单介绍
- 实现的功能:可以提交表单和文件
- 缺点:目前兼容性还不太好,因为 FormDate 这个类是比较新的特性,IE8 及之前版本的浏览器不支持
实现
前端Ajax发送数据的代码:
<h1>利用 Script.form.js 提交文件和表单数据</h1>
<div>
<form method="post" action="/ajax-upload/" enctype="multipart/form-data">
上传头像:
<input type="file" name="sub_file_name" />
<input type="button" value="提交" />
<input type="text" name="one" value="FormDataone"/>
<input type="text" name="two" value="FormDatatwo"/>
</form>
</div>
<script>
// script_form.js
$("#formjs_btn").on('click', function () {
var value = $("#sub_file").val()
if (!value) {
alert("请先选择文件");
return false;
}
if (!value.match(/.jpg|.jpeg|.gif|.png|.bmp/i)) {
alert("文件格式错误");
return false;
}
var inp_one = $("input[type='text']").first().val();
var inp_two = $("input[type='text']").last().val();
var option = {
url: '/ajax-upload/',
type: 'POST',
dataType: 'json',
data: {'one': inp_one, 'two': inp_two},
headers: { // 把认证信息添加到请求的头部,也能实现认证
"X-CSRFToken": $.cookie('csrftoken') // 从 cookie 中获取到csrf的值
}, // 注意这里的 key 必须是这个,这是Django 定义好的
success: function (data) {
alert(JSON.stringify(data)); // js 序列化数据,把数据对象转换为字符
},
error: function (data) {
alert("--上传失败,请刷新后重试");
}
};
$("#form0").ajaxSubmit(option); // 使用 script.form.js 上传数据和文件
return false; // 返回false阻止表单再次提交
});
</script>
后端接收数据的代码
- 后端接收数据的代码和第一种或者第二种一样
后端得到的数据
reqpost数据>>: <QueryDict: {'two': ['FormDatatwo', 'FormDatatwo'], 'one': ['FormDataone', 'FormDataone']}>
reqfile数据>>: <MultiValueDict: {'sub_file_name': [<InMemoryUploadedFile: 美女.jpg (image/jpeg)>]}>
接收到的文件>>: 美女.jpg
参数key one 的值>>: FormDataone
参数key two 的值>>: FormDatatwo