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){
        // 错误时处理逻辑
        }
            });

img

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 请求需要正确认证才能得到正确的返回结果。

解决的思路是:

  1. 在前端代码的 form 表单内写上 {% csrf_token %},
    用来接收 Django 返回的 csrf_token 的值
    {% csrf_token %} 会在表单中生一个隐藏的 input 标签

<input type="hidden" name="csrfmiddlewaretoken" value="EAYZaWHO8g7ZtI4N2xPzKCvxj94sCNPnsYTCLQpQWudH38iK0vHoyYf8NtRBjcRP">
\2. 想办法把此标签的 key 和 value 作为首层数据发送到后端,Django 后端会自动获取验证

获取到 csrf_token 的 key 和 value 的方法有一下几种

  1. 从页面中的源码中获得。
    当第一次请求后不论验证结果如何,前端页面在写上 {% csrf_token %}
    的前提下,就会有个隐藏的 input 标签,复制他的 name 属性的值就是 key, 复制 value属性的值就是我们要的 value
  2. 从cookie 中得到。
    利用 javascript.cookie.js 可以从 cookie 中得到。
  • javascript.cookie.js 下载地址
    就在其源码的 src 目录下 。

    使用方法:
    // 引入
    <script src="/static/jquery.cookie.js"></script> 
    <script>
    // 获取到值
    var token_val = $.cookie('csrftoken')
    </script>
    
  1. 从 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 标签内的所有数据,并进行序列化;这种适合提交数据比较多的情况
  1. 利用 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
posted @ 2024-05-21 14:45  欲过天空  阅读(98)  评论(0编辑  收藏  举报