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>
原文地址:https://www.cnblogs.com/yzm1017/p/11674502.html