本文主题
对于还不是很明白前后端分离的Web应用开发的朋友,可以通过本文,了解一下通过前端访问的URL路径,是如何一步步找到后端去执行具体函数的。
在这个过程中,主要聊到如下主题,您感兴趣就随便看看:
- 初遇路由
- 蓝图的注册
- Vue单页面应用
- Vue-cli配置文件中的跨域和路由
- 路由和视图函数
〇、前提
项目相关环境
前端:http://127.0.0.1:8010
后端:http://127.0.0.1:8089
一、路由的秘密
很多地方都会用到路由的概念,也很难一句话解释清楚,大家应该知道有个叫“路由器”的东西,数据从一个网络到另一个网络就是靠路由来完成的。
引用一张图来解释路由(出处:https://www.jianshu.com/p/b55cf53e633a):
- 先说一个现象
当我们在前端页面
http://127.0.0.1:8010/#/manage/flowerManage
进行某个操作时,在调试窗口看到它访问了这样一个接口并且得到了响应:
http://127.0.0.1:8010/api/flower/proGather/list
试试直接在Postman这种接口测试工具里输入该接口,也得到了对应的Response。
不过,在这个项目中,是在后端写了/proGather/list
路径对应的视图函数,执行后返回处理结果。
疑问产生
后端是在8089端口启的服务,为什么这里使用8010端口也能访问呢?如果直接改成8089端口,可以访问到嘛?那我们再来试试在Postman中访问这个接口:
http://127.0.0.1:8089/api/flower/proGather/list
哈哈,出错啦。不过注意到没有,报了这样的错误:
raise NotFound()\nwerkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
2.为什么呢?
简单解释就是:前端服务通过路由,找到后端对应的视图函数,进行处理。这里又有一个视图函数的概念哎!本文后面会解释一下这个,你就先理解成是一个函数,运行之后给出了返回值作为Response给到前端就好了。
接下来看看后端为啥没有访问成功,这个是和我们视图函数的写法,以及配置息息相关。因此会放到后面“Vue-cli配置文件”的部分详细解释清楚,先来说下针对这个项目,如何可以访问成功:
http://127.0.0.1:8089/flower/proGather/list
去掉路径里面的api
,就可以了。
二、蓝图
概念大家都懂了哇?说一下怎么用。一个WebApp里面可以注册一个蓝图,当然也可以注册多个蓝图,下面的例子就是注册多个蓝图的情况。
在create_app
的定义里面注册就好了,然后可以指定对应的url_prefix
。
在__init__.py
文件的create_app()
中,写了相关代码如下:
def create_app():
app = Flask(__name__)
app.config.from_object(BaseConfig)
app.logger.addHandler(config_log()) # 初始化日志
BaseConfig.init_app(app)
moment.init_app(app)
db.app = app
db.create_all()
login_manager.init_app(app)
scheduler.start() # 定时任务启动
# 注册多个蓝图模块
from app.admin import admin_module
from app.flower import flower_module
app.register_blueprint(admin_module, url_prefix='/admin')
app.register_blueprint(flower_module, url_prefix='/flower')
return app
那么再来看看蓝图模块是怎么写的呢?
目录结构是这个样子的:
app
-admin
__init__.py
admin_manage.py
-flower
__init__.py
flower_manage.py
__init__.py
看下admin目录下的两个文件;flower目录下也是类似的
app/admin/__init__.py
from flask import Blueprint
admin_module = Blueprint('admin_module', __name__)
from . import admin_manage
app/admin/admin_manage.py
from . import admin_module
@admin_module.route('/admin/add', methods=['POST'])
def add_user():
pass
好啦,照葫芦画瓢,你也会注册蓝图模块了!
三、理解Vue的单页面应用
很久以前用html做过网站,所以一直有一个惯性思维,觉得点击网页上的超链接跳来跳去的,就一定是在不同的页面间切换。
所以一开始也按照这个思路来学习Vue,看到别人案例中,有一个个的Vue文件,想当然的就理解成对应的页面了,后来才发现并非如此。
组件
Vue中有一个很重要的概念就是组件!官网都是这么介绍组件的:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: ''
})
new Vue({ el: '#components-demo' })
组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
你可以将组件进行任意次数的复用。
对了单页面应用简称SPA。罗里吧嗦一大堆,那么它和单页面应用之间有什么联系?
首先,我们定义组件;
然后,我们定义路由规则;
接着,我们访问路径,就会跳转到相应的组件上去。
所以,我们并不是在页面间跳转,而是通过路由规则,在组件之间进行跳转!
四、Vue-cli配置文件
在这个项目里,前端使用了Vue-cli脚手架。
如果你也直接用Vue-cli创建一个vue项目的话,可以在config目录下看到好几个.js文件,就是配置文件啦。
如果你是网上找来的项目,可能会略有不同,不过内容上都是类似的。
这次我们就来看看index.js
文件里的部分内容吧,由于把配置详解一遍很花时间,所以我们就聚焦一点,来揭开第一部分接口调用路径的谜底!
当然,有些项目的配置不一定保持了初始的名字,总之是含有module.exports = {
这行代码的配置文件就对了!
我这里是这样配置的:
module.exports = {
lintOnSave: true,
productionSourceMap: false,
chainWebpack: (config) => {
config.resolve.alias
.set('~', resolve('src'))
},
devServer: {
host: '127.0.0.1',
port: 8010,
proxy: {
'/api/': {
target: 'http://127.0.0.1:8089',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
css: {
loaderOptions: {
css: {
localIdentName: process.env.node_env == 'production' ? "[hash]" : '[path]_[name]_[local]_[hash:base64:5]',
camelCase: 'only'
},
sass: {}
},
}
};
首先看下这个devServer.proxy.changeOrigin
,它是为了解决跨域问题的一个设置。
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
那什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域,如下例子:
域名:
主域名不同 http://www.baidu.com/index.html –>http://www.sina.com/test.js
子域名不同 http://www.666.baidu.com/index.html –>http://www.555.baidu.com/test.js
域名和域名ip http://www.baidu.com/index.html –>http://180.149.132.47/test.js
端口:
http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
协议:
http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
备注:
1、端口和协议的不同,只能通过后台来解决
2、localhost和127.0.0.1虽然都指向本机,但也属于跨域
综上,我目前的设置前后端的端口不一样,也算是跨域了,因此就要设置成true啦!
接着看下这段:
proxy: {
'/api/': {
target: 'http://127.0.0.1:8089',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
vue-cli的这个设置来自于其使用的插件http-proxy-middleware,github地址如下:
https://github.com/chimurai/http-proxy-middleware
官网文档中有这么一句:
proxy('/api', {...}) - matches paths starting with /api
这就说明,当我们的路径中含有/api
的时候,就会匹配到这里对应的配置啦。
那么pathRewrite
是干啥的呢?文档解释如下:
option.pathRewrite: object/function, rewrite target's url path. Object-keys will be used as RegExp to match paths.
并且给出示例:
// rewrite path
pathRewrite: {'^/old/api' : '/new/api'}
// remove path
pathRewrite: {'^/remove/api' : ''}
// add base path
pathRewrite: {'^/' : '/basepath/'}
// custom rewriting
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }
而我们的配置中有写pathRewrite: { '^/api': '' }
,因此proxy时要把路径中的/api去掉。
OK,那么整体来解释一下devServer这段配置的意思!
devServer: {
host: '127.0.0.1',
port: 8010,
proxy: {
'/api/': {
target: 'http://127.0.0.1:8089',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
首先,127.0.0.1:8010
过来的请求,如果路径中含有/api
,就会被路由到127.0.0.1:8089
上去,并且把路径中的/api
去掉。
也就是说,当我们在前端访问了一个这样的接口:
http://127.0.0.1:8010/api/flower/proGather/list
实际上对应的是后端这个接口:
http://127.0.0.1:8089/flower/proGather/list
再来回到开头的问题上来,为什么使用postman访问http://127.0.0.1:8089/api/flower/proGather/list
会报404的错误?这下你就清楚了吧!
那么再来问一个问题,后端的接口路径为什么是这样的呢,那就要来聊聊视图函数啦。
五、路由与视图函数
在《Flask Web开发》一书中,对于URL如何映射到Python函数,有这样的一段解释:
客户端(例如Web浏览器)把请求发送给Web服务器,Web服务器再把请求发送给Flask程序实例。程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个URL到Python函数的映射关系。处理URL和函数之间关系的程序称为路由。
在Flask程序中定义路由的最简便方式,是使用程序实例提供的app.route修饰器,把修饰的函数注册为路由。
再来看我们的项目代码。还记得刚才说的目录结构么?
app
-admin
__init__.py
admin_manage.py
-flower
__init__.py
flower_manage.py
__init__.py
看一下flower_manage.py
这个文件:
from . import flower_module
@flower_module.route('/proGather/list')
def get_list():
pass
看来真正访问的是get_list()
这个函数呢!可是我们在修饰器里只写了相对路径/proGather/list
呀!
再来复习一下我们在create_app()
函数里面注册蓝图的时候是怎么写的:
from app.flower import flower_module
app.register_blueprint(flower_module, url_prefix='/flower')
所以flower_module
对应着url_prefix='/flower'
,在拼上后端服务启动时的端口号,可不就是http://127.0.0.1:8089/flower/proGather/list
了么!
六、小结
本文差不多就是这样了,如果还觉得看得不够清楚,下次我写一个示例项目传到github上再解说吧。