使用 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

原文发布于微信公众号 - 随心DevOps(heart-devops)

原文发表时间:2018-02-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏区块链

Web安全常见漏洞修复建议

看各大发布漏洞的平台,发现众多挖洞大神精彩的漏洞发掘过程,但在修复建议或者修复方案处,给出千奇百怪神一般的回复,故而总结一下修复建议(才疏学浅不算太全敬请谅解,...

1886
来自专栏安恒网络空间安全讲武堂

上传绕过总结

1.前端代码 2.逻辑绕过 3.文件内容检测 4.文件包含、文件备份 5.容器及语言特性 6.畸形报文 4.文件包含、文件备份 8.SQLI方面 上传文件时WA...

1808
来自专栏大数据智能实战

protobuf 转换python代码时发生 Expected "required", "optional", or "repeated".错误解决方法

        Google Protocol Buffers 简称 Protobuf,它提供了一种灵活、高效、自动序列化结构数据的机制,可以联想 XML,但是...

1838
来自专栏技术博客

ExtJs十(ExtJs Mvc用户管理之二)

为了防止意外情况,这里添加了try模块,在错误的时候会将错误信息作为Msg关键字的值返回。

782
来自专栏LeoXu的博客

NDK学习笔记(三)SWIG:自动生成JNI代码

SWIG 的全称是 Simplified Wrapper and Interface Generator,它是一个开发工具,在Android Native开发中...

923
来自专栏向治洪

android打包方法超过65k错误

近日,Android Developers在Google+上宣布了新的Multidex支持库,为方法总数超过65K的Android应用提供了官方支持。 如果...

1645
来自专栏FreeBuf

安全科普:利用WireShark破解网站密码

当我们输入账号、密码登录一个网站时,如果网站允许你使用HTTP(明文)进行身份验证,那么此时捕获通信流量非常简单,我们完全可以对捕获到的流量进行分析以获取登录账...

1725
来自专栏用户2442861的专栏

python flask音频流/文件服务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haluoluo211/article/d...

772
来自专栏進无尽的文章

扒虫篇 - 定位授权前地图定位造成的黑屏

公司一款App使用到了地图定位,可是从AppStore下载下来后,第一次安装完后出现了黑屏的现象。

842
来自专栏刘远飞的专栏

Nginx 封锁恶意 IP,并且定时取消的两种脚本

有时候服务器会遭受到恶意IP访问,导致服务器负载很大,这个时候需要把这个IP封锁掉,以下两种脚本即可实现这种功能。一是直接通过nginx即可,在配置文件中加入i...

8610

扫描关注云+社区