0%

前后端分离

该前后端分离的技术栈分别是

  • flask
  • vue

一个粗糙的例子


vue

关于 vue 这边的话,主要是下面的控件

  • axios

这里创建一个最初始化的 vue 项目,然后更改 src/components/HelloWorld.vue,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div>
{{ msg }}
</div>
</template>

<script>
import axios from 'axios'

export default {
name: "Ping",
data() {
return {
msg: ""
}
},
// 生命周期函数,加载页面时会先执行这个函数,从而调用 getMessage() 方法
created() {
this.getMessage();
},
methods: {
getMessage() {
// 使用 axios 向 flask 发送请求
const url = "http://127.0.0.1:5000/api/ping";
axios.get(url)
.then((res) => {
this.msg = res.data;
})
.catch((error) => {
console.log(error);
})
}

}
}
</script>

<style scoped>

</style>

flask

前后端两个独立的程序运行在不同的端口,要想相互交互,必须要解决跨域问题,后端程序需要安装:

1
pip install flask-Cors

弄一个简单的 flask 后台程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)


@app.route("/api/ping")
def home_show():
return "123"


if __name__ == '__main__':
CORS(app)
app.run(debug=True, host='127.0.0.1')

运行前端项目:npm run dev,访问:http://localhost:8080/#/

就会发现 123 已经出现在网页上了。

这只是一个非常粗糙的例子,后面我会逐步进行改进!

上面的例子有几个缺陷

  • 服务器实现跨域(CROS通信)
  • vue 中直接写上相关的请求地址,不优雅

略微改进后的例子


  • 后端只做后端的事,不再处理跨域问题,使用 vue 自身来处理跨域问题
  • 使前端编写代码更加优雅

ps:2020-01-08

vue 的跨域用的是 webpack-dev-server ,更深的了解,你可以在我下面的博文中可以看到

配置文件

我们将从 ***/src/config/index.js 这个文件来设置跨域配置。

下面的教程我认为并不具备一般性,因为,我使用的工程目录是 vue 自己生成的,如果换了版本,可能就不是这个目录和相关路由配置了,但是,这些都不是啥大问题,只要掌握一般性原理就可以应对了。

我此时的工程目录是「省略大部分文件,只展示相关文件」

---build
|-----webpack.dev.conf.js
---config
|-----index.js

webpack.dev.conf.js 文件中,我有这样的一些语句

1
2
3
4
5
6
7
8
9
10
11
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
...
// these devServer options should be customized in /config/index.js
devServer: {
...
proxy: config.dev.proxyTable,
...
},
...
})

上面删了大部分配置,上面的主要意思是,如果是使用

npm run dev

来本地测试的话,会加载 devWebpackConfig 配置,而关于 devServer 的相关属性主要在 /config/index.js,其中,proxy 这个属性对应于 config.dev.proxyTable

我们打开 config/index.js 发现有下面的配置

1
2
3
4
5
6
module.exports = {
dev: {
...
proxyTable: {},
},
}

删除大量的配置,其中 proxyTable 就是用来配置代理选项,解决跨域问题的。

翻阅官方文档,找到的配置解释如下

然后,我再来参阅一下,网上的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 配置多个代理
proxy: {
"/api": {
target: "<url>",// 要访问的接口域名
ws: true,// 是否启用websockets
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {
'^/api': '' //这里理解成用'/api'代替target里面的地址,比如我要调用'http://40.00.100.100:3002/user/add',直接写'/api/user/add'即可
}
},
"/foo": {
target: "<other_url>"
}
}

具体的原理是: vue 自己会构建一个虚拟的服务器来解决跨域问题。

好好的看上面的注释,在这里,我将对自己的配置进行如下改变:

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
proxyTable: {
"/api":{
target: 'http://127.0.0.1:5000',
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {
'^/api': ''
}
}
},
}

这里要注意的是,如果是请求 url 的第一个域如果是 /api 的话,那就得走这个代理

比如:

  • 127.0.0.1:5000/api/api/ping 其实,真正的路径是 127.0.0.1:5000/api/ping

之所以多出来一个 api 是因为在上面 proxy 的配置中有一个重写机制。

然后我们再次更改 HelloWorld.vue 的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div>
111{{ msg }}
</div>
</template>

<script>
import axios from 'axios'

export default {
data() {
return {
msg: ""
}
},
// 生命周期函数,加载页面时会先执行这个函数,从而调用 getMessage() 方法
created() {
this.getMessage();
},
methods: {
getMessage() {
// 使用 axios 向 flask 发送请求
const url = "/api/api/ping";
axios.get(url)
.then((res) => {
this.msg = res.data;
})
.catch((error) => {
console.log(error);
})
}

}
}
</script>

<style scoped>

</style>

可以看到有一个明显的变化是

1
const url = "/api/api/ping";

这个是和上面的配置有关的

如果按照 url = "/api/api/ping" 来发送请求的话,因为第一个域是 /api ,所以,其 /api 会被替换成 "" ,然后前面加上 http://127.0.0.1:5000

然后,最后的访问 url 就是

  • http://127.0.0.1:5000/api/ping

而,后端的代码只有简单的

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask

app = Flask(__name__)


@app.route("/api/ping")
def home_show():
return "123"


if __name__ == '__main__':
app.run(debug=True, host='127.0.0.1')

多代理服务器

假设,我们要进行多代理服务器的配置,那么,我们的各种文件如下

config/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
proxyTable: {
"/api":{
target: 'http://127.0.0.1:5000',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
},
"/app":{
target: 'http://127.0.0.1:5001',
changeOrigin: true,
pathRewrite: {
'^/app': ''
}
},
},
}

然后我们定义两个 vue 文件,一个是 HelloWorld.vue 另一个是 HellowWorld1.vue

其中 HelloWorld.vue 和上面一样,我们来贴一下 HellowWorld1.vue

HellowWorld1.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div>
222{{ msg }}
</div>
</template>

<script>
import axios from 'axios'

export default {
data() {
return {
msg: ""
}
},
// 生命周期函数,加载页面时会先执行这个函数,从而调用 getMessage() 方法
created() {
this.getMessage();
},
methods: {
getMessage() {
// 使用 axios 向 flask 发送请求
const url = "/app/api/ping";
axios.get(url)
.then((res) => {
this.msg = res.data;
})
.catch((error) => {
console.log(error);
})
}

}
}
</script>

<style scoped>

</style>

这两个文件最大的区别就是 url

然后,后端文件,一个就是上面,另一个我贴一下。

另一个后端文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)


@app.route("/api/ping")
def home_show():
return "hello world"


if __name__ == '__main__':
# CORS(app)
app.run(debug=True, host='127.0.0.1', port=5001)

最后的最后,我附上我的源码供参考

下载源码

还是建议下载源码来看。


需要注意的点


vue + axios + flask 「post 传递参数,后台获取为空」

axios不同于普通的Ajax,这表现在,当发起Ajax时,post的数据其实是一个FormData,而axios则是一个PayLoad,所以,在接收数据的方法上略有不同。

  • Ajax的接收方法是
    • request.form.get('aa')
    • 或者直接resquest.form['aa']
  • axios
    • request.get_json(silent=True)['aa']
    • 也可以先通过 request.get_data()获取到数据,然后再通过json.loads()转换为dict格式 「推荐使用这个,更加清晰」

跨域的 proxy 设置失效

可能有以下几个问题

  • proxyTable 配置错误
  • 电脑开着全局代理
  • 删掉 node_modules 目录,重新 npm install
  • 端口设置不对,如你的代理是 5001 ,但是,你的服务器端口是 8888
  • 重启电脑「有时候就是很奇怪,但是,重启电脑就能好」
请我喝杯咖啡吧~