質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Vuex

Vuexは、Vue.js アプリケーションのための状態管理ライブラリです。アプリケーション内で使用するコンポーネントのための集中データストアを提供。コンポーネント同士でデータをやり取りし、処理のフローを一貫させたり、データの見通しを良くすることができます。

Vue CLI

Vue CLIは、Vue.jsでアプリケーション開発を行うためのコマンドラインインタフェース(CLI)に基づいた開発ツールです。インタラクティブなプロジェクトの雛形や設定なしで使用できるプロトタイプの作成など、さまざまな機能が用意されています。

REST

REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

Q&A

0回答

2382閲覧

DjangoとVue.jsの連携で、403エラーが出る。

退会済みユーザー

退会済みユーザー

総合スコア0

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Vuex

Vuexは、Vue.js アプリケーションのための状態管理ライブラリです。アプリケーション内で使用するコンポーネントのための集中データストアを提供。コンポーネント同士でデータをやり取りし、処理のフローを一貫させたり、データの見通しを良くすることができます。

Vue CLI

Vue CLIは、Vue.jsでアプリケーション開発を行うためのコマンドラインインタフェース(CLI)に基づいた開発ツールです。インタラクティブなプロジェクトの雛形や設定なしで使用できるプロトタイプの作成など、さまざまな機能が用意されています。

REST

REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

0グッド

0クリップ

投稿2021/05/18 08:50

編集2021/05/19 10:43

前提・実現したいこと

  • 下記の本を参考にしながら、Django Rest FrameworkとVue.jsの連携について学んでいます。

現場で使える-Django-Framework-(Django-の教科書シリーズ)

  • バックエンドをDjango、フロントエンドをVueCli3を使って実装するというサンプルをやっています。

  • なお、全てのコードは、下記に載っていました。

https://github.com/akiyoko/drf-vue-sample

発生している問題・エラーメッセージ

  • 一通り実装を終えて、最初にログインボタンを押してみると、下記のエラーが出ます。
http://127.0.0.1:8000/auth/jwt/create/ 403 (Forbidden)

エラー内容

  • ユーザー名、パスワードは合っているので、JWT認証に関する部分が問題なのだと思いますが、サンプルのコードと見直しても、どこに誤りがあるのか分かりません。

  • Djangoの問題なのか、Vue.jsの問題なのか分かりません。

  • ひょっとしたら、axiosに関してCSRFエラーが出ているのかもしれませんが、対処が良く分からないです。

Django + axiosでCSRFエラーが発生する場合の対処方法

  • どなたか、お気づきの点がありましたら、教えて頂ければ幸いです。

該当のソースコード

  • ソースコードとして、どのファイルをお見せして良いのか分からないので、関係ありそうだと思ったものを貼らせて頂きます。
  • その他も、下記のコードを元に作成しています。

https://github.com/akiyoko/drf-vue-sample

javascript

1<!--project/frontend/src/pages/LoginPage.vue--> 2<!--ログイン画面コンポーネント--> 3<template> 4 <div id="login-page"> 5 <GlobalHeader/> 6 <GlobalMessage/> 7 <!-- メインエリア--> 8 <main class="container"> 9 <p class="h5 mb-4">Login</p> 10 <b-form @submit.prevent="submitLogin"> 11 <div class="row form-group"> 12 <label class="col-sm-3 col-form-label">user name</label> 13 <div class="col-sm-8"> 14 <b-form-input type="text" v-model="form.username" required/> 15 </div> 16 </div> 17 <div class="row form-group"> 18 <label class="col-sm-3 col-form-label">password</label> 19 <div class="col-sm-8"> 20 <b-form-input type="password" v-model="form.password" required/> 21 </div> 22 </div> 23 <div class="row text-center mt-5"> 24 <div class="col-sm-12"> 25 <b-button type="submit" variant="dark">Login</b-button> 26 </div> 27 </div> 28 </b-form> 29 </main> 30 </div> 31</template> 32<script> 33import GlobalHeader from "@/components/GlobalHeader.vue" 34import GlobalMessage from "@/components/GlobalMessage.vue" 35 36export default { 37 components: { 38 GlobalHeader, 39 GlobalMessage 40 }, 41 data() { 42 return { 43 form: { 44 username: '', 45 password: '' 46 } 47 } 48 }, 49 methods: { 50 //ログインボタン押下 51 submitLogin: function () { 52 //ログイン 53 this.$store.dispatch('auth/login', { 54 username: this.form.username, 55 password: this.form.password 56 }) 57 .then(() => { 58 console.log('Login succeeded.') 59 this.$store.dispatch('message/setInfoMessage', {message: 'You are loggedin.'}) 60 //クエリ文字列に「next」が無ければ、ホーム画面へ 61 const next = this.$route.query.next || '/' 62 this.$router.replace(next) 63 }) 64 } 65 } 66} 67</script>

javascript

1<!--project/frontend/src/store.js--> 2import Vue from 'vue' 3import Vuex from 'vuex' 4import api from '@/services/api' 5 6Vue.use(Vuex) 7 8// 認証情報 9const authModule = { 10 strict: process.env.NODE_ENV !== 'production', 11 namespaced: true, 12 state: { 13 username: '', 14 isLoggedIn: false 15 }, 16 getters: { 17 username: state => state.username, 18 isLoggedIn: state => state.isLoggedIn 19 }, 20 mutations: { 21 set (state, payload) { 22 state.username = payload.user.username 23 state.isLoggedIn = true 24 }, 25 clear (state) { 26 state.username = '' 27 state.isLoggedIn = false 28 } 29 }, 30 actions: { 31 /** 32 * ログイン 33 */ 34 login (context, payload) { 35 return api.post('/auth/jwt/create/', { 36 'username': payload.username, 37 'password': payload.password 38 }) 39 .then(response => { 40 // 認証用トークンをlocalStorageに保存 41 localStorage.setItem('access', response.data.access) 42 // ユーザー情報を取得してstoreのユーザー情報を更新 43 return context.dispatch('reload') 44 }) 45 }, 46 /** 47 * ログアウト 48 */ 49 logout (context) { 50 // 認証用トークンをlocalStorageから削除 51 localStorage.removeItem('access') 52 // storeのユーザー情報をクリア 53 context.commit('clear') 54 }, 55 /** 56 * ユーザー情報更新 57 */ 58 reload (context) { 59 return api.get('/auth/users/me/') 60 .then(response => { 61 const user = response.data 62 // storeのユーザー情報を更新 63 context.commit('set', { user: user }) 64 return user 65 }) 66 } 67 } 68} 69 70// グローバルメッセージ 71const messageModule = { 72 strict: process.env.NODE_ENV !== 'production', 73 namespaced: true, 74 state: { 75 error: '', 76 warnings: [], 77 info: '' 78 }, 79 getters: { 80 error: state => state.error, 81 warnings: state => state.warnings, 82 info: state => state.info 83 }, 84 mutations: { 85 set (state, payload) { 86 if (payload.error) { 87 state.error = payload.error 88 } 89 if (payload.warnings) { 90 state.warnings = payload.warnings 91 } 92 if (payload.info) { 93 state.info = payload.info 94 } 95 }, 96 clear (state) { 97 state.error = '' 98 state.warnings = [] 99 state.info = '' 100 } 101 }, 102 actions: { 103 /** 104 * エラーメッセージ表示 105 */ 106 setErrorMessage (context, payload) { 107 context.commit('clear') 108 context.commit('set', { 'error': payload.message }) 109 }, 110 /** 111 * 警告メッセージ(複数)表示 112 */ 113 setWarningMessages (context, payload) { 114 context.commit('clear') 115 context.commit('set', { 'warnings': payload.messages }) 116 }, 117 /** 118 * インフォメーションメッセージ表示 119 */ 120 setInfoMessage (context, payload) { 121 context.commit('clear') 122 context.commit('set', { 'info': payload.message }) 123 }, 124 /** 125 * 全メッセージ削除 126 */ 127 clearMessages (context) { 128 context.commit('clear') 129 } 130 } 131} 132 133const store = new Vuex.Store({ 134 modules: { 135 auth: authModule, 136 message: messageModule 137 } 138}) 139 140export default store 141

javascript

1<!--project/frontend/src/services/api.js--> 2import axios from 'axios' 3import store from '@/store' 4 5const api = axios.create({ 6 baseURL: process.env.VUE_APP_ROOT_API, 7 timeout: 5000, 8 headers: { 9 'Content-Type': 'application/json', 10 'X-Requested-With': 'XMLHttpRequest' 11 } 12}) 13 14// 共通前処理 15api.interceptors.request.use(function (config) { 16 // メッセージをクリア 17 store.dispatch('message/clearMessages') 18 // 認証用トークンがあればリクエストヘッダに乗せる 19 const token = localStorage.getItem('access') 20 if (token) { 21 config.headers.Authorization = 'JWT ' + token 22 return config 23 } 24 return config 25}, function (error) { 26 return Promise.reject(error) 27}) 28 29// 共通エラー処理 30api.interceptors.response.use(function (response) { 31 return response 32}, function (error) { 33 console.log('error.response=', error.response) 34 const status = error.response ? error.response.status : 500 35 36 // エラーの内容に応じてstoreのメッセージを更新 37 let message 38 if (status === 400) { 39 // バリデーションNG 40 let messages = [].concat.apply([], Object.values(error.response.data)) 41 store.dispatch('message/setWarningMessages', { messages: messages }) 42 43 } else if (status === 401) { 44 // 認証エラー 45 const token = localStorage.getItem('access') 46 if (token != null) { 47 message = 'ログイン有効期限切れ' 48 } else { 49 message = '認証エラー' 50 } 51 store.dispatch('auth/logout') 52 store.dispatch('message/setErrorMessage', { message: message }) 53 54 } else if (status === 403) { 55 // 権限エラー 56 message = '権限エラーです。' 57 store.dispatch('message/setErrorMessage', { message: message }) 58 59 } else { 60 // その他のエラー 61 message = '想定外のエラーです。' 62 store.dispatch('message/setErrorMessage', { message: message }) 63 } 64 return Promise.reject(error) 65}) 66 67export default api

python

1# config/urls.py 2from django.contrib import admin 3from django.urls import path, re_path, include 4from django.views.generic import TemplateView, RedirectView 5 6urlpatterns = [ 7 path('admin/', admin.site.urls), 8 path('', TemplateView.as_view(template_name='index.html')), 9 path('api/v1/auth/', include('djoser.urls')), 10 path('api/v1/auth/', include('djoser.urls.jwt')), 11 path('api/v1/', include('apiv1.urls')), 12 re_path('', RedirectView.as_view(url='/')), 13] 14

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問