前提・実現したいこと
基本的に一つのウィンドウでいろいろ(表示とか作成とか)をやるwebアプリを作成しています。
バックエンドにはdjangoを使用し、認証と、apiの提供などをします。
そして、index.htmlに
<div id="root"></div>
をおいて、またmain.js
を読み込み、トップページ上でreactアプリを動かすようにします。
main.js
は、react-reduxによって作成したものをyarnによってビルドし、ハッシュ値を取り除いた上でdjangoの静的ファイルが読み込める場所にシンボリックリンクを貼っています。
djangoで、あるモデル(Postします)の一覧情報を取得するAPIをrest_frameworkを用いて作成しました。これはログイン状態でないと403になり、ログイン状態ならばログイン中のUserに紐づくPostの一覧がjsonで返ってくるものです。
react-reduxのアプリで、このAPIを叩いて、その内容をactionに入れてdispatchして、それをstateに入れて表示して……とやりたいです。
発生している問題・エラーメッセージ
実際には、yarn start
で起動したhttp://localhost:3000
で見ることができるreact-reduxのアプリでそのAPIを叩くと、ステータスコード403で、
{"detail":"認証情報が含まれていません。"}
と返ってきてしまいます。あらかじめ別タブでログインしておいてもこうなってしまいます。また、同じくログイン状態で、APIを直接叩いたときはちゃんと情報が取得できますし、ビルドしてdjango側のページから参照されるようにしたものをhttp://localhost:8000
から見たときもちゃんと情報がstateに入っているようです。
該当のソースコード
django側
python
1# model.py 2 3from django.db import models 4from django.utils import timezone 5from django.contrib.auth.models import User 6 7class Post(models.Model): 8 title = models.CharField(max_length=200) 9 body = models.CharField(max_length=1000) 10 user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='users') 11 created_at = models.DateTimeField(auto_now_add=True) 12 updated_at = models.DateTimeField(auto_now=True) 13 14 def __str__(self): 15 return self.title
python
1# serializers.py 2 3from rest_framework import serializers 4from .models import Post 5 6class Meta: 7 model = Post 8 fields = ('id', 'title', 'body')
python
1# apis.py 2 3from rest_framework import viewsets 4from rest_framework.permissions import IsAuthenticated 5from .models import Post 6from .serializers import PostSerializer 7 8 9class PostViewSet(viewsets.ModelViewSet): 10 permission_classes = (IsAuthenticated, ) 11 serializer_class = PostSerializer 12 13 def get_queryset(self): 14 return Post.objects.filter(user=self.request.user)
python
1# urls.py 2 3from rest_framework import routers 4from .apis import PostViewSet 5 6 7router = routers.DefaultRouter() 8router.register('posts', PostViewSet, base_name='post')
python
1# settings.pyはlocalとproductionを分けるためにbase.pyで共通のものを、local.pyとproduction.pyで差分を定義しています。 2# 以下はapi関連に関して変更した部分を抜き出したものです。 3 4INSTALLED_APPS = [ 5 'post.apps.PostConfig', 6 'accounts.apps.AccountsConfig', // ログイン、ログアウトのためのアプリ 7 'django.contrib.admin', 8 'django.contrib.auth', 9 'django.contrib.contenttypes', 10 'django.contrib.sessions', 11 'django.contrib.messages', 12 'django.contrib.staticfiles', 13 'rest_framework', 14 'corsheaders' 15] 16 17CORS_ORIGIN_ALLOW_ALL = True # local.pyのみ
react側
javascript
1// index.js 2import React from 'react'; 3import ReactDOM from 'react-dom'; 4import './index.css'; 5import App from './App'; 6import registerServiceWorker from './registerServiceWorker'; 7import { createStore, applyMiddleware } from 'redux' 8import rootReducer from './reducers/index' 9import { Provider } from 'react-redux' 10import createSagaMiddleware from 'redux-saga' 11import { rootSaga } from './sagas' 12import { createLogger } from 'redux-logger' 13 14const sagaMiddleware = createSagaMiddleware() 15const middlewares = [sagaMiddleware] 16if(process.env.NODE_ENV !== 'production'){ 17 const loggerMiddleware = createLogger() 18 middlewares.push(loggerMiddleware) 19} 20const store = createStore(rootReducer, applyMiddleware(...middlewares)) 21 22sagaMiddleware.run(rootSaga, store.dispatch) 23 24ReactDOM.render( 25<Provider store={store}> 26 <App /> 27</Provider>, 28document.getElementById('root')); 29 30registerServiceWorker();
javascript
1// sagas.js 2import * as effects from 'redux-saga/effects' 3import * as call_api from './call_api' 4import * as actions from './actions' 5 6export function* setup(){ 7 const { success, error } = yield effects.call(call_api.post_list) 8 if(success && !error){ 9 yield effects.put(actions.set_post_list(success)) 10 } 11} 12 13export function* rootSaga(dispatch){ 14 yield effects.fork(setup) 15}
javascript
1// call_api.js 2const api_root = process.env.REACT_APP_API_ROOT // http://localhost:8000が入っています 3 4export const site_list = () => { 5 return fetch(api_root + '/api/posts.json', { mode: 'cors' }) 6 .then(response => response.json()) 7 .then((json) => { 8 return { success: json } 9 }).catch((error) => { 10 return { error } 11 }) 12}
javascript
1// actions.js 2 3import * as action_types from './constants/action_types' // export const SET_POST_LIST = 'SET_POST_LIST'とあります 4 5export const set_post_list = (post_list) => { 6 return { type: action_types.SET_POST_LIST, post_list } 7}
試したこと
上記でも書きましたが直接apiを叩いたり、buildしたものを見てみたりしました。その場合はyarn start
の環境のみで動きませんでした。
一応chromeとsafariで試しましたがだめでした。
シークレットウィンドウでlocalhost:8000
でログインしたあと同一タブでlocalhost:3000
にいってみましたがだめでした。
最初の状態ではCORSエラーが出たので、django-cors-headersを入れてそれを回避しています。
gitでどこでエラーが出るようになったのかをさかのぼってみましたが、APIに認証を付けた瞬間からエラーが出るようになったようです。
補足情報(FW/ツールのバージョンなど)
macOS Hight Sierra バージョン10.13.6
google chrome: 69.0.3497.100(Official Build)
Python 3.7.0
venvによる仮想環境下
asn1crypto==0.24.0 cffi==1.11.5 cryptography==2.3.1 Django==2.1 django-cors-headers==2.4.0 django-filter==2.0.0 djangorestframework==3.8.2 idna==2.7 pycparser==2.18 PyMySQL==0.9.2 pytz==2018.5 selenium==3.14.1 six==1.11.0 urllib3==1.23
yarn 1.9.2
"dependencies": { "prop-types": "^15.6.2", "react": "^16.5.2", "react-dom": "^16.5.2", "react-redux": "^5.0.7", "react-scripts": "1.1.5", "redux": "^4.0.0" }, "devDependencies": { "enzyme": "^3.6.0", "enzyme-adapter-react-16": "^1.5.0", "redux-logger": "^3.0.6", "redux-mock-store": "^1.5.3", "redux-saga": "^0.16.0" }
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。