• Django之Ajax


    Django之Ajax

    1.Ajax简介

    ​ AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。

      AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

      AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

      AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

        a.同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;

        b.异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。

      AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程

    示例

    通过ajax访问登陆页面,将获取到的用户名跟密码提交到后台进行校验,校验后跳转到home页面

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {#<form action="" method="post">#}
    {#    用户名:#}
    {#    <input type="text" id="username">#}
    {#    密码:#}
    {#    <input type="password" id='password'>#}
    {#    <button id="sub">提交</button>#}
    {#    <span class="error"></span>#}
    {#</form>#}
    <h1>用户登录</h1>
    用户名:<input type="text" id="username">
    密码:<input type="password" id='password'>
    <button id="sub">提交</button>
    <span class="error"></span>
    
    </body>
    <script src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        $('#sub').click(function () {
            $.ajax({
                url:'{% url 'login' %}',
                type:'post',
                data:{username:$('#username').val(),password:$('#password').val()},
                success:function (res) {
                    if(res==='1'){
                        $('.error').text('登陆成功');
                        location.href='/home/'
                    }else{
                        $('.error').text('用户名或密码错误')
                    }
                }
            })
        })
    </script>
    </html>
    

    urls.py路由分发

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/', views.login,name='login'),
        url(r'^home/', views.home),
    ]
    
    

    views.py视图函数

    def login(request):
        if request.method == 'GET':
            return render(request,'login.html')
        else:
            print(request.POST)
            uname = request.POST.get('username')
            pwd = request.POST.get('password')
            if uname == 'alex' and pwd == 'alex123':
                return HttpResponse('1')
            else:
                return HttpResponse('0')
    
    def home(request):
        return HttpResponse('home')
    

    启动项目后查看运行效果,发现页面不刷新,实现了局部刷新

    Ajax常见应用场景

    搜索引擎根据用户输入的关键字,自动提示检索关键字.注册时候的用户名查重就使用了Ajax技术,当文件框发生了输入变化,使用Ajax技术向服务区发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后把后端返回的结果展示出来

    a.整个过程中页面没有刷新,只是刷新了页面中的局部位置而已

    b.当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应

    img

    当输入用户名后,把光标移动到其他表单项上时,浏览器会使用Ajax技术向服务器发出请求,服务器查询名为lemontree7777777的用户是否存在,最终服务器返回true表示名为lemontree7777777的用户已经存在了,浏览器在得到结果后显示'用户名已经被注册'

    整个过程页面没有刷新,只是局部刷新了,在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作

    Ajax的优缺点

    优点:

    1.Ajax使用javascript技术向服务器发送异步请求

    2.Ajax请求无需刷新整个页面

    3.服务器响应内容不再是整个页面,而是整个页面中的部分内容,所以Ajax的性能高

    缺点:

    1.Ajax并不适合于所有场景,很多时候还是要使用同步交互

    2.Ajax虽然提高了用户体验.但是无形中向服务器发送的请求次数增多了,导致服务器的压力增大

    3.Ajax是在浏览器中使用JavaScript技术完成的,所以还需要处理浏览器兼容问题

    2.Ajax请求设置csrf_token

    ​ 详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。

    img

    所以解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。

    那么django中csrf认证怎么玩的呢?

      官方文档中说到,检验token时,只比较secret是否和cookie中的secret值一样,而不是比较整个token。

    部分源码

    def _compare_salted_tokens(request_csrf_token, csrf_token):
        # Assume both arguments are sanitized -- that is, strings of
        # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS.
        return constant_time_compare(
            _unsalt_cipher_token(request_csrf_token),
            _unsalt_cipher_token(csrf_token),
        )
    
    def _unsalt_cipher_token(token):
        """
        Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length
        CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt
        the second half to produce the original secret.
        """
        salt = token[:CSRF_SECRET_LENGTH]
        token = token[CSRF_SECRET_LENGTH:]
        chars = CSRF_ALLOWED_CHARS
        pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt))
        secret = ''.join(chars[x - y] for x, y in pairs)  # Note negative values are ok
        return secret
    

    ​ token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。
      django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
      同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。

    常见的网络请求安全解决办法
    
        Cookies Hashing:每一个表单请求中都加入随机的Cookie,由于网站中存在XSS漏洞而被偷窃的危险。 
        HTTP refer:可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。 
        验证码:用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。 
        令牌Token:一次性令牌在完成他们的工作后将被销毁,比较安全。
        ...等等吧,还有很多其他的。
    

    设置csrf_token

    方式1

    通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送

    #form表单类型
    <form action="" method="post">
        {% csrf_token %}
        用户名:<input type="text" id="username">
        密码:<input type="password" id='password'>
        <button id="sub">提交</button>
        <span class="error"></span>
    </form>
    
    #非form表单类型
    <h1>用户登录</h1>
    {% csrf_token %}
    用户名:<input type="text" id="username">
    密码:<input type="password" id='password'>
    <button id="sub">提交</button>
    <span class="error"></span>
    
    
    
    
    
    $('#sub').click(function(){
        $.ajax({
            url: '{% url "login" %}',
            type: 'post',
            data: {username: $('#username').val(), password: $('#password').val(), csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val()},
            success: function (res) {
                console.log(res);
                if (res === '1'){
                    $('.error').text('登陆成功');
                    location.href = '/home/'
                 } else {
                    $('.error').text('用户名或密码错误')
              }
           }
        })
    })
    

    方式2:

    #  csrfmiddlewaretoken:'{{ csrf_token }}
    
    
    $('#sub').click(function(){
      $.ajax({
          url: '{% url "login" %}',
          type: 'post',
          data: {
    username: $('#username').val(), 
    password: $('#password').val(), 			csrfmiddlewaretoken:'{{ csrf_token }}'},
          success: function (res) {
              console.log(res);
              if (res === '1'){
                  $('.error').text('登陆成功');
                  location.href = '/home/'
              } else {
                  $('.error').text('用户名或密码错误')
              }
          }
      })
    })
    

    方式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就可以这样加
    })
    
    
    
    $('#sub').click(function () {
        $.ajax({
            url: '{% url 'login' %}',
            type: 'post',
            data: {username: $('#username').val(), password: $('#password').val()},
            headers: {'X-CSRFToken': $.cookie('csrftoken'),},
            success: function (res) {
                if (res === '1') {
                    $('.error').text('登陆成功');
                    location.href = '/home/'
                } else {
                    $('.error').text('用户名或密码错误')
                }
            }
        })
    })
    

    form表单设置csrf_token

    <form action="" method="post">
        {% csrf_token %}  
    // form表单里面加上这个标签,模板渲染之后就是一个input标签,type=hidden name=csrfmiddlewaretoken value='asdfasdfasdf'
        用户名: <input type="text" name="username">
        密码: <input type="password" name="password">
        <input type="submit">
    
    </form>
    

    3.Ajax文件上传

    基于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")
    

    unload函数

    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))
            #>>> 1.txt         <class'django.core.files.uploadedfile.InMemoryUploadedFile'>一个文件对象,可以理解为一个文件句柄
            file_name = file_obj.name #1.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不是固定长度的,和读取其他文件一样,每次读一行,识别符为
      
      
    ,遇到这几个符号就算是读了一行
    
    
           for chunks in file_obj.chunks(): 
        		   f.write(chunks)
        #chunks()默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器
             
    

    通过js寻找文件对象

    img

    基于Ajax的文件上传

    upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        用户名:<input type="text" id="username">
        密码:<input type="password" id='password'>
        文件: <input type="file" name="file">
        <button id="sub">提交</button>
        <span class="error"></span>
    </form>
    
    </body>
    <script src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        
        $('#sub').click(function () {
            var formdata = new FormData();
            var uname = $('#username').val();
            var pwd = $('#password').val();
            var file_obj = $('[type=file]')[0].files[0];  //js获取文件对象
            var csrfmiddlewaretoken = {{ csrf_token }};
            formdata.append('username',uname);
            formdata.append('password',pwd);
            formdata.append('file',file_obj);
            formdata.append('csrfmiddlewaretoken',csrfmiddlewaretoken);
    
            $.ajax({
                url: '{% url "login" %}',
                type: 'post',
                data:formdata,
                processData:false,   //必须写
                contentType:false,   //必须写
                success: function (res) {
                    console.log(res);
                    if (res === '1') {
                        $('.error').text('登陆成功');
                        location.href = '/home/'
                    } else {
                        $('.error').text('用户名或密码错误')
                    }
                }
            })
        })
    </script>
    </html>
    

    views.py

    def upload(request):
        if request.method == 'GET':
            return render(request,'upload.html')
        else:
            print(request.POST)
            print(request.FILES)
            uname = request.POST.get('username')
            pwd = request.POST.get('password')
            file_obj = request.FILES.get('file')   #文件对象
            print(file_obj.name)     #文件名称
            with open(file_obj.name,'wb') as f:
                for i in file_obj:
                    f.write(i)
            return HttpResponse('okk')
    

    4.关于json

    1.什么是json?

    • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)

    • JSON 是轻量级的文本数据交换格式

    • JSON 独立于语言 *

    • JSON 具有自我描述性,更易理解

    • JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。

    img

    json数据类型和python数据类型的对比:

    img

    ​ 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字符串,在进行序列化的时候的区别

    import json
    # s = "{'name':'chao','age':18}" 
    #普通字符串,每加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。
    s = '{"name":"chao","age":18}'   #json字符串,里面必须是双引号
    ret = json.loads(s)
    print(ret)
    print(ret['name'])
    

    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"})
    
    

    前后端使用json

    方式一

    jsontest.htnl

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户登录</h1>
    {% csrf_token %}
    用户名:<input type="text" id="username">
    密码:<input type="password" id='password'>
    <button id="sub">提交</button>
    <span class="error"></span>
    </body>
    
    
    
    <script src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        $('#sub').click(function () {
            var uname = $('#username').val();
            var pwd = $('#password').val();
            $.ajax({
                url:'{% url "jsontest" %}',
                type:'post',
                data:{username:uname,password:pwd,csrfmiddlewaretoken:'{{ csrf_token }}'},
                    success:function (res) {
                    console.log(res,typeof res);
                    var res = JSON.parse(res);
                    if(res.status === 1000){
                        location.href='/home/'
                    }else{
                        $('.error').text(res.msg)
                    }
                }
    
            })
    
        })
    </script>
    </html>
    
    

    views.py

    import json
    def jsontest(request):
        '''
        状态码:
        1000:登陆成功
        1001:登陆失败
        :param request:
        :return:
        '''
        if request.method == 'GET':
            return render(request,'jsontest.html')
        else:
            uname = request.POST.get('username')
            pwd = request.POST.get('password')
            ret_data = {'status':None,'msg':None}
            print('>>>>',request.POST)
            if uname == 'alex' and pwd == 'alex123':
                ret_data['status'] = 1000
                ret_data['msg'] = '登陆成功'
            else:
                ret_data['status'] = 1001
                ret_data['msg'] = '登陆失败'
            ret_data_json = json.dumps(ret_data,ensure_ascii=False)
            return HttpResponse(ret_data_json)
    
    

    方式二

    jsontest.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户登录</h1>
    {% csrf_token %}
    用户名:<input type="text" id="username">
    密码:<input type="password" id='password'>
    <button id="sub">提交</button>
    <span class="error"></span>
    </body>
    
    
    
    <script src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        $('#sub').click(function () {
            var uname = $('#username').val();
            var pwd = $('#password').val();
            $.ajax({
                url:'{% url "jsontest" %}',
                type:'post',
                data:{username:uname,password:pwd,csrfmiddlewaretoken:'{{ csrf_token }}'},
                    success:function (res) {
                    console.log(res,typeof res);
                    if(res.status === 1000){
                        location.href='/home/'
                    }else{
                        $('.error').text(res.msg)
                    }
                }
    
            })
    
        })
    </script>
    </html>
    
    

    views.py

    import json
    def jsontest(request):
        '''
        状态码:
        1000:登陆成功
        1001:登陆失败
        :param request:
        :return:
        '''
        if request.method == 'GET':
            return render(request,'jsontest.html')
        else:
            uname = request.POST.get('username')
            pwd = request.POST.get('password')
            ret_data = {'status':None,'msg':None}
            print('>>>>',request.POST)
            if uname == 'alex' and pwd == 'alex123':
                ret_data['status'] = 1000
                ret_data['msg'] = '登陆成功'
            else:
                ret_data['status'] = 1001
                ret_data['msg'] = '登陆失败'
            ret_data_json = json.dumps(ret_data,ensure_ascii=False)
            return HttpResponse(ret_data_json,content_type='application/json')
    
    

    方式三

    jsonresponse

    jsontest.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>用户登录</h1>
    {% csrf_token %}
    用户名:<input type="text" id="username">
    密码:<input type="password" id='password'>
    <button id="sub">提交</button>
    <span class="error"></span>
    </body>
    
    
    
    <script src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        $('#sub').click(function () {
            var uname = $('#username').val();
            var pwd = $('#password').val();
            $.ajax({
                url:'{% url "jsontest" %}',
                type:'post',
                data:{username:uname,password:pwd,csrfmiddlewaretoken:'{{ csrf_token }}'},
                    success:function (res) {
                    console.log(res,typeof res);
                    if(res.status === 1000){
                        location.href='/home/'
                    }else{
                        $('.error').text(res.msg)
                    }
                }
            })
        })
    </script>
    </html>
    
    

    views.py

    from django.http import JsonResponse
    def jsontest(request):
        '''
        状态码:
        1000:登陆成功
        1001:登陆失败
        :param request:
        :return:
        '''
        if request.method == 'GET':
            return render(request,'jsontest.html')
        else:
            uname = request.POST.get('username')
            pwd = request.POST.get('password')
            ret_data = {'status':None,'msg':None}
            print('>>>>',request.POST)
            if uname == 'alex' and pwd == 'alex123':
                ret_data['status'] = 1000
                ret_data['msg'] = '登陆成功'
            else:
                ret_data['status'] = 1001
                ret_data['msg'] = '登陆失败'
            return JsonResponse(ret_data)
        
        #非字典类型的数据需要给JsonResponse加上 safe=False 参数
    
    
  • 相关阅读:
    官场22条潜规则,职场谁说不是呢
    pomelo使用中的常见问题
    马斯诺需求金字塔
    Mac上使用brew安装nvm来支持多版本的Nodejs
    Redis 集群解决方案 Codis
    Linux下压缩某个文件夹(文件夹打包)
    使用forever运行nodejs应用
    nodejs npm常用命令
    Mac安装Brew
    C#2.0 迭代器
  • 原文地址:https://www.cnblogs.com/tutougold/p/11658655.html
Copyright © 2020-2023  润新知