zoukankan      html  css  js  c++  java
  • 11 Django -- Ajax/CSRF以及文件上传

    Ajax

    Ajax简介

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

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

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

    特点:异步请求、局部刷新

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

    局部刷新:可以局部添加网页内容,如提示:用户名或者密码错误等。

    AJAX常见应用情景

    1. 搜索引擎根据用户输入的关键字,自动提示检索关键字。

    2. 注册时的用户名的查重。

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

      a.整个过程中页面没有刷新,只是局部刷新了;

      b.在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作;

    3. 提交数据,上传文件。

    Ajax的优缺点

    https://www.cnblogs.com/SanMaoSpace/archive/2013/06/15/3137180.html

    优点:

    1. AJAX使用JavaScript技术向服务器发送异步请求;
    2. AJAX请求无须刷新整个页面;
    3. 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;

    缺点:

    1. 安全问题,不经意间会暴露比以前更多的数据和服务器逻辑
    2. 如果用户禁用了JS,网站就取不到数据
    3. AJAX干掉了Back和History功能,即对浏览器机制的破坏。用户通常会希望单击后退按钮能够取消他们的前一次操作,但是在Ajax应用程序中,这将无法实现。
    4. AJAX不能很好支持移动设备。

    Ajax简单登录认证

    用form表单登录,提交数据会刷新页面。ajax不会刷新。

    login.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
    </head>
    <body>
    <div style=" 500px;height:200px;background-color: #4ba7ff;margin-top: 180px; margin-left: 350px;">
        <div style="padding-top:50px;">
        <div class="form-group">
            <label class="col-sm-2 control-label">用户名</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" placeholder="请输入用户名" id="username">
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">密码</label>
            <div class="col-sm-10">
                <input type="password" class="form-control" placeholder="请输入密码" id="password">
            </div>
        </div>
    
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default" id="sub">登 录</button>
                <span id="error" style="color: red"></span>
            </div>
        </div>
        </div>
    </div>
    </body>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
    
    <script >
    
        $('#sub').click(function () {
    		// 绑定点击事件,一点击 登录 会将数据提交给后端,不会刷新页面
            var username=$('#username').val();
            var password=$('#password').val();
    
            $.ajax({
                url:'{% url "login" %}',	//
                type:'post',
                data:{'user':username,'pwd':password},      
                // data会携带数据提交到url路径   res:是接收后端返回的数据
                success:function (res) {
                    if(res === '1'){
                        // 表示登录成功,跳转到home页面
                        location.href='{% url "home" %}'}	
                    else{
                        // 登录失败,局部刷新,添加提示
                        $('#error').text('用户名或密码错误!')
                    }
                }
            })
        })
    </script>
    </html>
    

    urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/', views.login, name='login'),
        url(r'^home/', views.home, name='home'),
    
    

    views.py

    from django.shortcuts import render, HttpResponse, redirect
    
    
    def login(request):
    
        if request.method=='GET':
            return render(request, 'login.html')
    
        else:
            user = request.POST.get('user')		# 获取ajax中data携带的数据
            pwd = request.POST.get('pwd')
           
            if user == 'yan' and pwd == '123':	# 假设用户名与密码
                return HttpResponse('1')		# 登录成功返回1
            else:
                return HttpResponse('0')		# 登录成功返回0
    
    
    def home(request):
    
        return render(request, 'home.html')
    

    csrf认证

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

    img

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

     token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。

    form表单设置csrf_token

    {% csrf_token %} 标签,加上这个标签,模板渲染之后就是一个隐藏的input标签,提交数据时,会随数据一起提交给后端。

    <input type="hidden" name=‘csrfmiddlewaretoken’ values=‘dkjdkasiofaad...’>

    <form action="" method="post">
        {% csrf_token %} // form表单里面加上这个标签,模板渲染之后就是input标签
    <div style=" 500px;height:200px;background-color: #4ba7ff;margin-top: 180px; margin-left: 350px;">
        <div style="padding-top:50px;">
        <div class="form-group">
            <label class="col-sm-2 control-label">用户名</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" placeholder="请输入用户名" id="username" name='username'>
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">密码</label>
            <div class="col-sm-10">
                <input type="password" class="form-control" placeholder="请输入密码" id="password" name='password'>
            </div>
        </div>
    
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default" id="sub">登 录</button>
                <span id="error" style="color: red"></span>
            </div>
        </div>
        </div>
    </div>
    
    </form>
    
    

    Ajax设置csrf认证

    方式一:

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

    // 在body标签中,增加 {% csrf_token %} 标签
    <script >
        $('#sub').click(function () {
    
            var username=$('#username').val();
            var password=$('#password').val();
            
            // csrf方式一:
            var csrf=$('[name=csrfmiddlewaretoken]').val();
            // 使用jQuery取出csrfmiddlewaretoken的值,
            $.ajax({
    
                url:'{% url "login" %}',
                type:'post',
                data:{'user':username,'pwd':password, "csrfmiddlewaretoken":csrf},      // data会携带数据
                success:function (res) {
                    if(res === '1'){
                        location.href='{% url "home" %}'}
                    else{
                        console.log(res);
                        $('#error').text('用户名或密码错误!')
                    }
                }
            })
        })
    </script>
    

    方式二:

    用 {{ csrf_token }} 变量直接获取csrfmiddlewaretoken的值

    <script >
    
        $('#sub').click(function () {
    
            var username=$('#username').val();
            var password=$('#password').val();
      
            // csrf方式二:
            var csrf = '{{ csrf_token }}';
            $.ajax({
    
                url:'{% url "login" %}',
                type:'post',
                data:{'user':username,'pwd':password, "csrfmiddlewaretoken":csrf},      // data会携带数据
                success:function (res) {
                    if(res === '1'){
                        location.href='{% url "home" %}'}
                    else{
                        console.log(res);
                        $('#error').text('用户名或密码错误!')
                    }
                }
            })
        })
    </script>
    
    

    方式三:

    引入:jquery.cookie.js;下载:http://plugins.jquery.com/cookie/

    通过获取返回的cookie中的字符串 放置在请求头中发送。

    ​ Ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,发contenttype类型数据的时候,csrf_token就可以这样加。

    <script >
    $('#sub').click(function () {
    
            var username=$('#username').val();
            var password=$('#password').val();
    
      
            $.ajax({
                url:'{% url "login" %}',
                type:'post',
                data:{'user':username,'pwd':password, "csrfmiddlewaretoken":csrf},      // data会携带数据
    
                // csrf 方式三:
                headers:{
                    "X-CSRFToken":$.cookie('csrftoken'),
                },      // 必须引入jquery.cookie.js
    
                success:function (res) {
                    if(res === '1'){
                        location.href='{% url "home" %}';
                        {#$('#error').text('登录成功!'); }#}
                        }
                    else{
                        {#console.log(res);#}
                        $('#error').text('用户名或密码错误!')
                    }
                }
            })
        })
    </script>
    

    注意:

      如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。

      如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。

      这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。

    django.views.decorators.csrf import ensure_csrf_cookie
    
    @ensure_csrf_cookie
    def login(request):
        pass
    

    文件上传

    请求头Content-Type

    服务端告诉浏览器,数据是以什么封装的。否则浏览器不知道怎样解开数据。

    1. Content-Type: application/x-www-form-urlencoded;charset=utf-8
    	# 最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。
    	user=yan&pwd=123   #这就是上面这种content-type规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,都是这样拼接数据,大家公认的一种数据格式.
    	
    2. Content-Type:multipart/form-data;	
    	# form表单设置enctype属性时,post提交数据的方式
    
    3. Content-Type:application/json	
    	# 告诉浏览器 服务端消息主体是序列化后的 JSON 字符串。
    

    form表单上传文件

    在form中加 enctype="multipart/form-data"。form表单不支持发json类型的content-type格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。

    <form action="" method="post" enctype="multipart/form-data">  
        {% csrf_token %}
        用户名: <input type="text" name="username">
        密码: <input type="password" name="password">
        文件: <input type="file" name="file"> 
    
        <input type="submit">
    
    </form>
    

    views.py

    from django.conf import settings
    # 能用全局settings获取一些配置,如路径等
    
    def upload(request):
    	if request.method == 'GET':
        	print(settings.BASE_DIR) #/static/
        	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)
                
                # 方式二:chunks()默认一次返回大小为65536B,也就是64KB,最大为2.5M的数据,是一个生成器
            	for chunk in file_obj.chunks():
                	f.write(chunk)
        	return HttpResponse('ok')
    

    通过js来找文件对象

    Ajax的上传文件

    <script >
    $('#sub').click(function () {
      		
        	// 创建一个对象
            var formdata = new FormData();
    	    
            var user = $('#username').val();
            var pwd = $('#password').val();
    
            var file_obj = $('[type=file]')[0].files[0]; // js获取文件对象
    		
        	// 封装
            formdata.append('username',user);
            formdata.append('password',pwd);
            formdata.append('file',file_obj);
    
            $.ajax({
                url:'{% url "upload" %}',
                type:'post',
                // data:{username:user,password:pwd,csrfmiddlewaretoken:csrf},
                //data:{username:uname,password:pwd},
                data:formdata,
                
                processData:false,  // 必须写,告诉不做任何预处理
                contentType:false,  // 必须写
    
                headers:{
                    "X-CSRFToken":$.cookie('csrftoken'),
                },
                success:function (res) {
                    console.log(res);
                    if (res === '1'){
                        // $('.error').text('登录成功');
                        location.href = '/home/'; // http://127.0.0.1:8000/home/
    
                    }else{
                        $('.error').text('用户名密码错误!');
                    }
                }
            })
        })
    </script>
    

    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)
                
                # 方式二:chunks()默认一次返回大小为65536B,也就是64KB,最大为2.5M的数据,是一个生成器
            	for chunk in file_obj.chunks():
                	f.write(chunk)
        	return HttpResponse('ok')
    

    JsonResponse

    发送json类型的数据

    from django.http import JsonResponse
    
    
            username = request.POST.get('username')
            pwd = request.POST.get('password')
            ret_data = {'status':None,'msg':None}
            print('>>>>>',request.POST)
            #<QueryDict: {'{"username":"123","password":"123"}': ['']}>
            if username == 'chao' and pwd == '123':
                ret_data['status'] = 1000  # 状态码
                ret_data['msg'] = '登录成功!'
    
            else:
                ret_data['status'] = 1001  # 状态码
                ret_data['msg'] = '登录失败!'
            # 方式一  ,关闭ensure_ascii后不乱码
            # ret_data_json = json.dumps(ret_data,ensure_ascii=False)
            # return HttpResponse(ret_data_json)
            # 方式二,指定响应头
        # return HttpResponse(ret_data_json,content_type='application/json')
        
            # 方式三
            return JsonResponse(ret_data)
        
    # 非字典类型数据,需要加上 safe=False参数,否则认为是不合法的
    		#ret_data=[0,1,'a']
    		#return JsonResponse(ret_data, safe=False)
    

    jsontest.html

    <script>
     $.ajax({
                url:'{% url "jsontest" %}',
                type:'post',
                // data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},
         //序列化       //data:JSON.stringify({username:uname,password:pwd}),
    
                data:{username:uname,password:pwd},
                headers:{
                    // contentType:'application/json',
                    "X-CSRFToken":$.cookie('csrftoken'),
                },
                success:function (res) {
                    {#console.log(res,typeof res); // statusmsg {"status": 1001, "msg": "登录失败"}#}
                    // 方式一:反序列化
                    {#var res = JSON.parse(res);  //-- json.loads()#}
                    // 方式二、三
                    console.log(res,typeof res);  //直接就是反序列化之后的了
                    //JSON.stringify()  -- json.dumps
                    if (res.status === 1000){
                        // $('.error').text('登录成功');
                        location.href = '/home/'; // http://127.0.0.1:8000/home/
    
                    }
                     else{                    		$('.error').text(res.msg);
                    }
                }
            })
            
    </script>
    

    SweetAlert插件--对话框

    img

    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.0-dist/dist/css/bootstrap.min.css' %}">
        <link rel="stylesheet" href="{% static 'bootstrap-sweetalert-master/dist/sweetalert.css' %}">
    </head>
    <body>
    
    <div>
        <button class="btn btn-danger">删除</button>
    </div>
    
    </body>
    <script src="{% static 'bootstrap-3.3.0-dist/dist/jQuery/jquery-3.1.1.js' %}"></script>
    <script src="{% static 'bootstrap-3.3.0-dist/dist/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'bootstrap-sweetalert-master/dist/sweetalert.js' %}"></script>
    <script>
    
        $(".btn-danger").on("click", function () {
            swal({
                    title: "你确定要删除吗?",
                    text: "删除可就找不回来了哦!",
                    type: "warning",
                    showCancelButton: true,
                    confirmButtonClass: "btn-danger",
                    confirmButtonText: "我已经下定决心",
                    cancelButtonText: "容我三思",
                    closeOnConfirm: false
                },
                function () {
                    var deleteId = $(this).parent().parent().attr("data_id");
                    $.ajax({
                        url: "/delete_book/",
                        type: "post",
                        data: {"id": deleteId},
                        success: function (data) {
                            console.log(data,typeof data);
                            if (data === '1') {
                                swal("删除成功!", "你可以准备跑路了!", "success");
                            } else {
                                swal("删除失败", "你可以再尝试一下!", "error")
                            }
                        }
                    })
                });
        })
    </script>
    </html>
    
  • 相关阅读:
    word2vec原理推导与代码分析
    vim 删除
    HanLP 配置与使用
    python import 其他 package的模块
    mysql 修改root密码
    Spring Boot 整合 PageHelper
    MyBatis SQL语句构建器
    webpack4
    MySql DCL数据控制语言(对用户权限的设置)
    MySql DQL数据查询语言
  • 原文地址:https://www.cnblogs.com/yzm1017/p/11674502.html
Copyright © 2011-2022 走看看