本篇介绍如何在vue端向django发送post请求,以及django处理post请求的方式
这次要实现的功能是:点击【身份证ID】生成指定数量的身份证号
1. 前端main_page.vue相关代码编写
<script>
import axios from 'axios'
import Qs from 'qs'
export default {
name: "main_page",
data() {
return {
num1: null,
num2: null,
info: null,
}
},
methods: {
create_data(event) {
if (event.target.id === "b01") { //通过event.target.id,获取浏览器监听到的点击事件,并查看点击元素的id,通过比对id值判断触发哪个请求
axios({
url: "http://localhost:8000/create_data/phone" //如果不指定method,默认发送get请求
}).then(res => {
this.info = res.data
console.log(res)
})
} else if (event.target.id === "b02") {
console.log('输入的num1='+this.num1)
let payload1 = {
num: this.num1,
}
axios({
method: "post",
headers: {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
},
data: Qs.stringify(payload1), //发送post请求,使用data关键字接收请求参数
url: "http://localhost:8000/create_data/id"
}).then(res => {
this.info = res.data
console.log(res)
})
} else if (event.target.id === "b03") {
let payload = {
num: this.num2
}
console.log(payload)
axios({
method: "get",
params: payload, //发送get请求,使用params关键字接收请求参数
url: "http://localhost:8000/create_data/name"
}).then(res => {
this.info = res.data
console.log(res)
})
}
},
},
}
</script>
1、首先导入的一个包 import Qs from 'qs'
2、axios下代码说明
method,指明了请求方法为 post;
headers,添加请求头,请求头中加了一个 Content-Type,为 application/x-www-form-urlencoded; charset=UTF-8;
data,添加携带的参数,这里对参数做了一个处理,使用 Qs.stringify(payload1)对payload进行了序列化处理(如果是application/x-www-form-urlencoded格式的数据,必须要做这个处理,不然django会获取不到请求body中的参数)
2. 后端代码
def create_id(num):
"""生成身份证"""
identity_ids = [fake.ssn() for i in range(int(num))]
return " ".join(identity_ids)
@require_http_methods(['GET', 'POST'])
def id(request):
num = request.POST.get("num") # "Content-type","application/x-www-form-urlencoded"
print(num)
if num == "" or num is None:
data1 = create_id(5)
else:
data1 = create_id(num)
return HttpResponse(data1)
post请求,如果请求头Content-type=application/x-www-form-urlencoded,可以使用request.POST.get("num"),获取请求携带的参数
ok,到这里先试一下看看有没有正常得到响应,页面点击按钮后如下
貌似还是跨域的问题, 我们继续在 settings.py中新增如下配置
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
然后再次发起请求,得到如下结果,仍然报错了
这次的错误是CSRF验证失败
通过查资料得知,这个是django特意加的一个csrf认证,当发送post请求,向服务器提交数据时都要做这个验证,很蛋疼~~
为了解决这个问题,我在网上冲浪了很久很久,终于找到了2个解决方法
解决django-csrf认证-方法1
最简单的方式就是关闭这个验证,把相关配置注释掉,即可跳过认证,自由的发送post请求(如果是自己学习的话,可以采用这个方式,以防心态炸裂,可以愉快地进行后续的学习~)
打开settinngs.py,找到如下代码,把我标记的那一行注释即可
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # 注释这一行可以去掉csrf验证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
解决django-csrf认证-方法2
网上的资料繁杂,很多博主给的解决方式都已经失效(不清楚是不是和版本有关),结合多篇博客,经过大量实践,终于找到一个可行的解决方法(虽然成功了,但是本人不理解其中的原理,可能需要扒源码才行)
上面我们给settings.py添加了一行配置:
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
这个配置不要动,继续保留
1、打开views.py,我们需要写一个方法来生成一个csrftoken
from django.middleware.csrf import get_token
def get_csrf_token(request):
csrf_token = get_token(request) # 获取csrf_token的值
return JsonResponse({'token': csrf_token})
给这个试图配置路由(等下前端需要调这个方法获取token)
create_data/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.main, name='main'),
path('phone', views.phone, name='phone'),
path('id', views.id,name='id'),
path('name', views.name,name='name'),
path('get_csrf_token', views.get_csrf_token,name='get_csrf_token'),
]
2、前端axios的配置修改
打开main_page.vue
首先在script标签下添加一行代码
axios.defaults.withCredentials = true #允许跨域携带cookie信息,必须加上
在script标签下新增一个函数token(),用来调用后台生成csrftoken的函数get_csrf_token()
methods: {
token() {
axios.get("http://localhost:8000/create_data/get_csrf_token").then(res => {
let token = res.data.token
console.log("**********"+token);
sessionStorage.setItem("csrf_token", token) //有些博主说把生成的token放到缓存中,然后发请求时拿这个缓存放到请求头中,我试了下这个其实并没有用,可以注释掉,因为我们不拿这个token
})
},
create_data(event) {
......
......
......
}
修改 create_data(event)函数中关于触发生成电话号码的axios请求
methods: {
create_data(event) {
this.token() //调用create_data()函数时,先调用token()函数,请求后台生成csrftoken
// console.log("..............."+this.token());
// console.log(typeof event.target.id)
console.log('cookie='+document.cookie) //打印浏览器cookie
let cookie = document.cookie //提取cookie
let csrf_token = cookie.split("=")[1] //提取cookie中的csrftoken
console.log('cookie_csrf_token='+cookie.split("=")[1])
if (event.target.id === "b01") {
......
......
} else if (event.target.id === "b02") {
console.log('输入的num1='+this.num1)
let payload1 = {
num: this.num1,
}
axios({
method: "post",
headers: {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRFToken': csrf_token,
},
data: Qs.stringify(payload1), //application/x-www-form-urlencoded,发送post请求,使用data关键字接收请求参数
url: "http://localhost:8000/create_data/id"
}).then(res => {
this.info = res.data
console.log(res)
})
} else if (event.target.id === "b03") {
......
......
}
}
}
(1)首先在create_data()函数下添加了一行代码,来调用刚才新增的token()函数
this.token()
(2)提取浏览器生成的cookie
let cookie = document.cookie // 通过document.cookie提取cookie
let csrf_token = cookie.split("=")[1] //提取cookie中的csrftoken
这个cookie应该是django服务器向客户端发送的,通过它来完成csrf验证,post请求必须拿到cookie中的csrftoken然后跟着请求一起发送才行!
(3)最后在headers中加一行
'X-CSRFToken': csrf_token
这个也必须加上,请求头中必须要有这个参数才能被django识别 然后再来尝试发送这个请求,成功了
看一下请求的详细内容
首先如果把token()函数注释掉,看看发送失败和发送成功请求的区别
可以看到,左图的请求中没有携带Cookie,而我们的csrf token是通过document.cookie来获取,所以左图中的X-CSRFToken为空;
网上有人说,可以把后台生成的csrftoken直接赋给请求头中的 X-CSRFToken,我试了一下并不行,还是会提示403Forbidden;
所以通过csrf认证的真正方式是:django配置好跨域允许携带cookie后,并且axios也配置好允许携带cookie,发送post请求时,Django会自动发给客户端一个cookie
我们需要把这个cookie中的csrftoken拿出来再赋给请求头中的 X-CSRFToken,这样才能通过csrf认证
打开chrom浏览器控制台,切换到Application,找到如下位置
发post请求时,这里会自动多出一个cookie,也就是csrftoken
可以自己试一下,如果把这个cookie删掉,发post请求就会报 403Forbidden
如果按照上述配置好的话,每次触发这个请求时,都会在这里自动生成一个cookie,猜测是Django自动发给客户端的
然后客户端需要携带这个cookie才能提高django的csrf验证
当然,如果不按照上述配置,例如 没有配置 axios.defaults.withCredentials = true 或者 前端没有调用后台生成csrftoken的方法,触发post请求时,django服务器便不会发给客户端这个cookie
网上也有博主说可以在axios请求中添加 withCredentials: true,如下形式
axios({
withCredentials: true,
method: "post",
headers: {
......
}
我也试了一下,发现不好用,还是得在顶部配置:axios.defaults.withCredentials = true
按照上述方法我成功解决了Django的csrf验证问题,不过每个人的环境可能不同, 其他人也有可能会遇到别的坑 唉,这个真是卡了我太久了