vue与django交互参考 《django+vue+vue-resource+django-cors-headers实现前后端分离》
vue+django django用的是vue build后的 dist目录 启动用django共用一个端口,不存在跨域问题
要实现csrf认证首先要了解django CSRF原理
CSRF跨域是不会自动生成csrftoken的(深坑,之前POST后端报错csrf not set 就是这个原因)
post请求前必须先get一次产生token
手动生成csrftoken
1request.META["CSRF_COOKIE_USED"] = True
2手动调用 csrf 中的 get_token(request) 或 rotate_token(request) 方法
3html表单带{%csrf_token%} (适用于django render渲染的前端)
在vivews.py设置
# Create your views here. from django.shortcuts import render,HttpResponse,redirect from blog.models import * from django.views.decorators.http import require_http_methods import json from django.core import serializers from django.http import JsonResponse from django.middleware.csrf import get_token ,rotate_token import simplejson @require_http_methods(["GET",\'POST\']) def add_book(request): response = {} try: if request.method==\'GET\': get_token(request) #或request.META["CSRF_COOKIE_USED"] = True 新加产生token if request.method==\'POST\': req = simplejson.loads(request.body) # json格式传过来的数 Book.objects.create( **req ) # Book.objects.create( #x-www-form-urlencoded传过来的数据 # **{"book_name":request.POST.get(\'book_name\')}, # ) response[\'msg\'] = \'success\' response[\'error_num\'] = 0 # if request.method==\'GET\': # # book = Book(book_name=request.GET.get(\'book_name\')) # # book.save() # Book.objects.create( # 方法二建议这种,直接将前端字典插入 # **{"book_name":request.GET.get(\'book_name\')}, # ) # response[\'msg\'] = \'success\' # response[\'error_num\'] = 0 except Exception as e: response[\'msg\'] = str(e) response[\'error_num\'] = 1 return JsonResponse(response) @require_http_methods(["GET",\'POST\']) def show_books(request): response = {} get_token(request) try: print(request.POST) print(request.body) books = Book.objects.filter() response[\'list\'] = json.loads(serializers.serialize("json", books)) response[\'msg\'] = \'success\' response[\'error_num\'] = 0 except Exception as e: response[\'msg\'] = str(e) response[\'error_num\'] = 1 return JsonResponse(response)
vue前端设定 main.js入口
import Vue from \'vue\' import App from \'./App\' import router from \'./router\' import VueResource from \'vue-resource\' Vue.use(VueResource); Vue.config.productionTip = false; //Vue.http.options.xhr = { withCredentials: true }; //Vue.http.options.emulateJSON = true; //Vue.http.interceptors.push(function(request, next) {//拦截器 // 跨域携带cookie // request.credentials = true; //next() //}); //vue未build跨域使用的,跨域建议代理。更简便 new Vue({ el: \'#app\', router, components: { App }, template: \'<App/>\' });
Vue.http.options.xhr = { withCredentials: true }; 的用途就是允许跨域请求携带cookie做身份认证
Vue.http.options.emulateJSON = true; 的作用是如果web服务器无法处理application/json的请求,你可以启用emulateJSON选项。启用该选项后请求会以application/x-www-form-urlencoded作为MIME type,就像普通的HTML表单一样
拦截器主要是作用是可以在请求前和发送请求后做一些处理
因为跨域请求是不会提供任何凭据的(cookie、HTTP认证及客户端SSL证明等),但是我们可以通过设置request.credentials = true;来表示允许
components/submit.vue
<template> <div id="tomato"> <form @submit.prevent="submit"> <div class="field"> 书名: <input type="text" v-model="book.book_name"> </div> <input type="submit" value="提交"> </form> <button v-on:click="display">搜索</button> <table v-for="item in books"> <tr> <td>书名:{{ item.fields.book_name}} && 添加时间:{{item.fields.add_time}}</td> </tr> </table> </div> </template> <script> export default { data (){ return { books: [], book:{ book_name:null, } } }, // mounted() { // // GET /someUrl // this.$http.get(\'http://127.0.0.1:8000/api/show_books\').then(response => { // this.books = response.data.list; // console.log(this.books[0].fields.book_name); // // get body data // // this.someData = response.body; // // }, response => { // console.log("error"); // }); // }, methods: { submit: function() { //post函数 var formData = JSON.stringify(this.book); // 这里才是你的表单数据 console.log(formData); this.$http.get(\'http://127.0.0.1:8000/api/add_book\'); //产生COOKIE 必须先get一次不然会报错csrf not set sleep(4000); //等级几秒等,不然可能获取不到cookie var DjangoCookie=getCookie(\'csrftoken\'); //发送过去的headers带上X-CSRFToken进行认证,值就是cookie csrftoken的值 this.$http.post(\'http://127.0.0.1:8000/api/add_book\',formData,{headers:{\'X-CSRFToken\':DjangoCookie}},{emulateJSON: true}).then((response) => { // success callback console.log(response.data); }, (response) => { console.log("error"); // error callback }); // this.$http({ //此方法可以通过CSRF认证但是数据过不去,未深究 // method:\'POST\', // url:\'http://127.0.0.1:8000/api/add_book\', // data:{\'book_name\':\'lxs\'}, // headers: {"X-CSRFToken": DjangoCookie}, // emulateJSON: true // }).then((response) => { // // success callback // console.log(response.data); // }, (response) => { // console.log("error"); // // error callback // }); }, display() { // GET /someUrl URL为django接口地址 this.$http.get(\'http://127.0.0.1:8000/api/show_books\').then(response => { this.books = response.data.list; console.log(this.books[0].fields.book_name); // get body data // this.someData = response.body; }, response => { console.log("error"); }); // this.$http({ //结果同上get // method:\'GET\', // url:\'http://127.0.0.1:8000/api/show_books\'}).then(response => { // this.books = response.data.list; // console.log(this.books[0].fields.book_name); // // get body data // // this.someData = response.body; // // }, response => { // console.log("error"); // }); }, }, } function getCookie(name){ //获取cookie函数 name = name + "="; var start = document.cookie.indexOf(name), value = null; if(start>-1){ var end = document.cookie.indexOf(";",start); if(end == -1){ end = document.cookie.length; } value = document.cookie.substring(start+name.length,end); } return value; } function sleep(numberMillis) { //等待函数 var now = new Date(); var exitTime = now.getTime() + numberMillis; while (true) { now = new Date(); if (now.getTime() > exitTime) return; } } </script>
vue-resource 引入headers 除了局部引入,还可以在全局引入(在main,js引入后所有post请求都只需提交数据就行)
import Vue from \'vue\' import App from \'./App\' import router from \'./router\' import VueResource from \'vue-resource\' import ElementUI from \'element-ui\'; import \'element-ui/lib/theme-chalk/index.css\'; import store from \'./store/store\' Vue.use(ElementUI); Vue.use(VueResource); Vue.config.productionTip = false; Vue.http.get(\'http://127.0.0.1:8000/api/add_book\').then(response => {}); let djangocookie=getCookie(\'csrftoken\'); Vue.http.headers.common[\'X-CSRFToken\'] = djangocookie;//这里设置请求头 router.beforeEach((to, from, next) => { if (to.path === \'/login\') { next() } else { if (!store.state.user ) { next({ path: \'/login\' }) } else { next() } } }); new Vue({ el: \'#app\', router, store, components: { App }, template: \'<App/>\' }); function getCookie(name){ //获取cookie函数 name = name + "="; let start = document.cookie.indexOf(name), value = null; if(start>-1){ let end = document.cookie.indexOf(";",start); if(end === -1){ end = document.cookie.length; } value = document.cookie.substring(start+name.length,end); } return value; }
OK, 结束,完美收工,差点被百度的坑埋了。。。。相当。。。。。。。。。。。。。