使用Django和Nuxt实现前后端分离
Author:
Satori
Posted: 2018-09-19 20:10:24
Views: 2195 Comments: 0
## 一、背景
在我搭建这个网站到后期的时候,明显感觉前端的代码维护难度太高。因为Django自带模板渲染是基于Jinja渲染的,最大的特点是模板中的数据从服务端一次取出,然后再渲染页面。而数据改变需要手动去维护依赖该数据的html段落。而且研发到后期发现很多前端模块都是可以抽象成一个组件,但是通过模板的include会带来各种问题。在有机会接触到一些前端的mvvm框架后我就起手打算改造网站,实现前后端分离。
## 二、整体思路
前端页面部分使用Nuxt,它是一个基于Vue的Server-Side-Render的解决方案,自动集成了vue, vue-router, vuex等等组件,能让前端的开发效率明显增高。同时后端使用Django作为服务的提供者。在服务端渲染的时候,如果需要异步数据,则先发请求给Django,然后的得到数据后再渲染成页面返回给浏览器。之后客户端如果有任何异步数据,都通过ajax的方式向Django发送请求来获得数据。
## 三、实现细节
1. 请求的发送
前端使用axios来发送请求。我的做法是封装一个api的基础client,这个client被各个业务线的其他client继承。这个基础的client主要负责封装请求的发送,以及异常的处理。
```js
import axios from 'axios';
class ApiClient {
get_axios_client () {
let axios_client = axios.create(this.client_config);
axios_client.interceptors.response.use(function (response) {
if (response.status !== 200) {
return Promise.reject(response.data);
}
return response.data;
}, function (error) {
error = error.response || error;
console.log('error', error);
if (!error.status) {
return Promise.reject({
code: 999,
message: 'Server Error!',
});
}
var message = '';
if (error.status >= 400 && error.status < 500) {
message = 'request error, message: ' + error.status + ': ' + error.statusText + '. ' + error.data.message;
} else if (error.status === 502 || error.status === 504) {
message = 'The server is busy or restarting';
} else {
message = 'Internal Server Error';
}
return Promise.reject({
code: 999,
message: message,
});
});
return axios_client;
}
constructor (config) {
this.client_config = config;
this.client = this.get_axios_client();
}
get (url, params, config) {
let mergedConfig = Object.assign({}, config, this.client_config, {url: url, params: params, method: 'get'});
return this.request(mergedConfig);
}
post (url, data, config) {
let mergedConfig = Object.assign({}, config, this.client_config, {url: url, data: data, method: 'post'});
return this.request(mergedConfig);
}
request (config) {
return this.client.request(config)
.then(response => {
return response;
})
.catch(response => {
console.log('Error Occurs. Response: ' + response);
return Promise.reject(response);
});
}
}
export default ApiClient;
```
这里主要封装了两个方法,一个是get方法,一个是post方法,都返回一个promise,并且增加一个拦截器,拦截response,在返回正常的情况下把data部分返回,在有问题的时候返回reject,并给出错误码和信息。
同时config里面可以设置请求一些参数,比如超时时间等等。
接着封装一个带有默认配置的BaseClient,设置了请求的基准url以及超时时间,代码如下。
```
import ApiClient from '@/utils/api_client';
const client_config = {
baseURL: '/api/',
timeout: 20000,
};
class BaseApiClient {
constructor (sub_url) {
const baseURL = client_config.baseURL + sub_url;
this.client = new ApiClient({...client_config, baseURL});
}
}
export default BaseApiClient;
```
之后所有业务都可以继承这个BaseClient即可实现请求的发送。
2. 请求的处理
请求处理这边就使用Django,通过cookie来识别用户,这部分的技术细节会在接下来的文章里提到。然后Django把请求处理完之后返回给前端数据,最后前端拿到数据做渲染。无论是服务器渲染还是客户端发的请求,都能够处理。
3. 前后端连接
前面都在讲前端如何发请求,后端怎么处理请求上面,最关键的地方还没有讲到,那就是怎么把前后端连接起来。
按照我现在的方案,node的端口在3000,Django的端口在8000(后期当然可以使用linux socket方式,不通过http来进行通信),那么从node的请求都会走3000这个端口,那Django怎么接收这个请求呢?答案就是nginx。nginx可以作为一个强大的反向代理服务器,我们可以这么操作。
先让nginx去监听某一个端口(比如80),然后根据url前缀进行请求分发。如果是前端的请求(页面请求),那么就代理到node开启的3000端口,其他的都丢给Django。
区分前端路由以及后端请求的方法很简单。对于api请求,我都在路由前面加一个`/api`,这个所有以这个请求开头的都是后端请求,其他都是前端请求。这样做也避免了跨域的问题。不过我这里想使用Django的Admin界面进行数据库的一些管理。因此我需要特别匹配一下`admin`这个路由到Django,同时Admin页面也会依赖一些静态资源,路由以`/static`开头,因此也需要匹配路由到Django。因此,nginx的配置文件大概是下面这样。
```
server {
listen 80;
server_name 127.0.0.1;
charset utf-8;
location /admin {
proxy_pass http://127.0.0.1:8000;
}
location /static {
alias /home/satori/new_mysite/new_mysite/backend/static; # This should be changed to your own static file folder
}
location /api {
proxy_pass http://127.0.0.1:8000;
}
location / {
proxy_pass http://127.0.0.1:3000;
}
}
```
这样配置所有的请求都能做正确的分发,因此前后端请求就连接上了,也就能通信了。
LOGIN TO LEAVE A COMMENT