使用 React 和 Django REST Framework 構建你的網站
在我們最近的工作中,構建網站使用的架構是帶有 Django REST Framework(DRF)後端的 React 前端。它們是通過在前端使用 axios(前端庫)呼叫後端 API 來互動的。我們還使用了 Redux(前端庫)來儲存全域性的應用程式狀態(存在瀏覽器端)。這是我們首選,因為它允許前後端完全分離。只要我們提前定義好請求的資源列表(後面單個都簡稱:endpoint)和返回的資料格式,前端和後端就可以並行的進行開發。這也使我們可以輕鬆的為未來的任何專案建立移動端 App,因為它們仍然可以複用後端 API。
在本文的剩餘部分,我將介紹如何配置 React 前端和 DRF 後端。注意我假設你已經熟悉了 React,Redux,Django,DRF,NPM 等,本篇不是基礎教程哦。
後端(The Backend)
除了簡單安裝 Django 和 DRF 以及設定資料庫以外,後端沒有太多的工作要做
$ pip3 install django djangorestframework django-filter
$ pip3 freeze > requirements.txt
沒錯我們用的是 Python3
找一個目錄,建立一個 Django 專案和 Django App:
$ django-admin startproject backend
$ cd backend
$ django-admin startapp api
接下來應該配置好你的資料庫並編輯你的專案 settings 檔案來使用它。在 Django 的官網上可以找到關於如何為你的特定 DB 執行此操作的文件。
或者你也可以什麼都不幹,它會幫你在專案跟目錄建立一個檔案資料庫:sqlite3.db
最後你還應該按照在這裡的說明配置一下 DRF。
有些同學對前後端分離的認證方式有些懵逼,我們下面就看一下前後端分離的架構如何配置認證後端:
# file: api/urls.py from django.conf.urls import url from rest_framework.authtoken import views as drf_views urlpatterns = [ url(r'^auth$', drf_views.obtain_auth_token, name='auth'),]
只是為了確保清楚,你的 backend/urls.py 檔案現在應該是這樣:
# file: backend/urls.py
from django.conf.urls import url, include
urlpatterns = [
url(r'^', include('api.urls', namespace='api', app_name='api')),]
通過這樣,我們可以讓每個應用程式管理自己的 URL。因為也許將來你會在後端新增更多的應用程式,並且將他們新增到 backend/urls.py。
現在,你已經擁有了一個後端 DRF API:叫 /auth 的 endpoint,訪問它可以獲得一個身份驗證令牌。讓我們先配置一個使用者,並執行後端伺服器以供測試。
$ python3 manage.py migrate
$ python3 manage.py createsuperuser
$ python3 manage.py runserver 0.0.0.0:8000
記得第一次要執行遷移命令(migrate)來建立你的資料庫。然後,我們建立一個使用者。在伺服器執行的情況下,你可以使用 curl 快速測試您的 /auth endpoint:
$ curl -X POST -d "username=username&password=password"
http://localhost:8000/auth
譯者在驗證過程中發現作者忽略了一些細節,補充如下
1.新增 rest_framework和rest_framework.authtoken 到 INSTALLED_APPS 配置:
# file: backend/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
]
2.執行命令建立認證 App 所需的資料庫:
$ python manage.py migrate
介面 I/O
到這裡,後端搞定
前端(The Frontend)
前端我們使用 Facebook 的 create-react-app 腳手架作為 base。首先要做的就是安裝它,然後在專案資料夾的根目錄下使用它來建立一個新的專案。
$ npm install -g create-react-app
$ create-react-app frontend
$ cd frontend
$ yarn eject
提示:執行 yarn eject 之前必須要提交所有 git 修改,因為它會更改你的檔案和新增目錄,怕你丟失之前的修改
接下來我們安裝一些依賴:
$ npm install --save-dev babel-preset-es2015 babel-preset-stage-3
$ npm install --save redux redux-logger redux-persist react-redux
$ npm install --save axios react-router-dom lodash
現在,我們先只展示將前端連線後端的主要部分。首先建立一個 redux store,用它來儲存使用者的 token,以便將來進行更多的API呼叫。
// file: src/store.js
import { compose, createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import { persistStore, autoRehydrate } from 'redux-persist';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
compose(
applyMiddleware(
createLogger(),
),
autoRehydrate()
));persistStore(store);
export default store;
然後配置 token 的 reducer:
// file: src/reducers/index.js
import { combineReducers } from 'redux';
import * as actionType from '../actions/types';
const tokenInitialState = null;
const token = (state = tokenInitialState, action) => {
switch(action.type) {
case actionType.SET_TOKEN:
return action.data;
default:
return state;
}
}
const appReducer = combineReducers({
token,
})
const rootReducer = (state, action) => {
return appReducer(state, action);
}
export default rootReducer;
最後是配置 action(注意程式碼塊有兩個不同的檔案)
// file: src/actions/index.js
import * as actionType from './types';
export const setToken = (data) => {
return {
type: actionType.SET_TOKEN,
data
}
}
// file: src/actions/types.js
export const SET_TOKEN = "SET_TOKEN";
有了一個 action,我們現在可以在登入後 dispatch 使用者的 token 到 store 中。我們看看如何登入:
// file: src/util/Auth.js
import axios from 'axios';
import _ from 'lodash';
import store from '../store';
import { setToken } from '../actions';
import { URL, LOGIN } from '../config/Api';
export function InvalidCredentialsException(message) {
this.message = message;
this.name = 'InvalidCredentialsException';
}
export function login(username, password) {
return axios.post(URL + LOGIN, {
username,
password
})
.then(function (response) {
store.dispatch(setToken(response.data.token));
})
.catch(function (error) {
// raise different exception if due to invalid credentials
if (_.get(error, 'response.status') === 400) {
throw new InvalidCredentialsException(error);
}
throw error;
});
}
export function loggedIn() {
return store.getState().token == null;
}
這段程式碼使用 axios POST 登入資訊到我們的 /auth endpoint,然後將返回的 token
dispatch 到我們的 redux store。一旦完成,我們就可以使用我們儲存的 token 令牌來建立一個基於 axios 的 API 客戶端(譯者注:這樣就不需要每次都顯式的將令牌資訊從 store 中拿出來再插入 payload 中了),這樣從我們的 React 元件中的其他地方進行其他 API 呼叫就很方便了。
// file: src/util/ApiClient.js
import axios from 'axios';
import store from '../store';
import { URL } from '../config/Api';
export const apiClient = function() {
const token = store.getState().token;
const params = {
baseURL: URL,
headers: {'Authorization': 'Token ' + token}
};
return axios.create(params);
}
前面的後兩個程式碼塊中我們引用瞭如下的 ../config/Api 檔案。它只是一個將常量對映到 endpoint 的檔案,它會使程式碼更易讀,更容易修改。(如果有些懵,可以參考譯者按照筆者的思路還原的程式碼,在文末)
export const URL = process.env.API_URL;
export const LOGIN = "/auth";
這就是一套連線我們的前端和後端的程式碼了。你現在可以嘗試使用 Auth.js 登入功能來獲取我們之前建立的使用者身份驗令牌。這樣可以通過從瀏覽器的開發者工具檢查 redux-logger 的輸出,來檢視 setToken redux 的 action 結果了。
目錄總覽
如果對一些細節不清楚,或者因為排版問題看不清,可以直接看原始碼:
https://github.com/tmpbook/django-with-vuejs/tree/master/examples/django-auth-with-react