共计 6119 个字符,预计需要花费 16 分钟才能阅读完成。
本文主题
对于还不是很明白前后端分离的 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: '<button v-on:click="count++">You clicked me {{count}} times.</button>'
})
<div id="components-demo">
<button-counter></button-counter>
</div>
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 上再解说吧。
作者:cynthia 猫
链接:https://www.jianshu.com/p/b72548f04592
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。