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, 結束,完美收工,差點被百度的坑埋了。。。。相當。。。。。。。。。。。。。
