本文主題
對(duì)于還不是很明白前后端分離的Web應(yīng)用開(kāi)發(fā)的朋友引有,可以通過(guò)本文,了解一下通過(guò)前端訪問(wèn)的URL路徑倦逐,是如何一步步找到后端去執(zhí)行具體函數(shù)的譬正。
在這個(gè)過(guò)程中,主要聊到如下主題檬姥,您感興趣就隨便看看:
- 初遇路由
- 藍(lán)圖的注冊(cè)
- Vue單頁(yè)面應(yīng)用
- Vue-cli配置文件中的跨域和路由
- 路由和視圖函數(shù)
〇曾我、前提
項(xiàng)目相關(guān)環(huán)境
前端:http://127.0.0.1:8010
后端:http://127.0.0.1:8089
一、路由的秘密
很多地方都會(huì)用到路由的概念健民,也很難一句話解釋清楚抒巢,大家應(yīng)該知道有個(gè)叫“路由器”的東西,數(shù)據(jù)從一個(gè)網(wǎng)絡(luò)到另一個(gè)網(wǎng)絡(luò)就是靠路由來(lái)完成的秉犹。
引用一張圖來(lái)解釋路由(出處:http://www.reibang.com/p/b55cf53e633a):
- 先說(shuō)一個(gè)現(xiàn)象
當(dāng)我們?cè)谇岸隧?yè)面
http://127.0.0.1:8010/#/manage/flowerManage
進(jìn)行某個(gè)操作時(shí)蛉谜,在調(diào)試窗口看到它訪問(wèn)了這樣一個(gè)接口并且得到了響應(yīng):
http://127.0.0.1:8010/api/flower/proGather/list
試試直接在Postman這種接口測(cè)試工具里輸入該接口,也得到了對(duì)應(yīng)的Response崇堵。
不過(guò)型诚,在這個(gè)項(xiàng)目中,是在后端寫(xiě)了/proGather/list
路徑對(duì)應(yīng)的視圖函數(shù)鸳劳,執(zhí)行后返回處理結(jié)果俺驶。
疑問(wèn)產(chǎn)生
后端是在8089端口啟的服務(wù),為什么這里使用8010端口也能訪問(wèn)呢棍辕?如果直接改成8089端口,可以訪問(wèn)到嘛还绘?那我們?cè)賮?lái)試試在Postman中訪問(wèn)這個(gè)接口:
http://127.0.0.1:8089/api/flower/proGather/list
哈哈楚昭,出錯(cuò)啦。不過(guò)注意到?jīng)]有拍顷,報(bào)了這樣的錯(cuò)誤:
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.為什么呢抚太?
簡(jiǎn)單解釋就是:前端服務(wù)通過(guò)路由,找到后端對(duì)應(yīng)的視圖函數(shù)昔案,進(jìn)行處理尿贫。這里又有一個(gè)視圖函數(shù)的概念哎!本文后面會(huì)解釋一下這個(gè)踏揣,你就先理解成是一個(gè)函數(shù)庆亡,運(yùn)行之后給出了返回值作為Response給到前端就好了。
接下來(lái)看看后端為啥沒(méi)有訪問(wèn)成功捞稿,這個(gè)是和我們視圖函數(shù)的寫(xiě)法又谋,以及配置息息相關(guān)拼缝。因此會(huì)放到后面“Vue-cli配置文件”的部分詳細(xì)解釋清楚,先來(lái)說(shuō)下針對(duì)這個(gè)項(xiàng)目彰亥,如何可以訪問(wèn)成功:
http://127.0.0.1:8089/flower/proGather/list
去掉路徑里面的api
咧七,就可以了。
二任斋、藍(lán)圖
概念大家都懂了哇继阻?說(shuō)一下怎么用。一個(gè)WebApp里面可以注冊(cè)一個(gè)藍(lán)圖废酷,當(dāng)然也可以注冊(cè)多個(gè)藍(lán)圖瘟檩,下面的例子就是注冊(cè)多個(gè)藍(lán)圖的情況。
在create_app
的定義里面注冊(cè)就好了锦积,然后可以指定對(duì)應(yīng)的url_prefix
芒帕。
在__init__.py
文件的create_app()
中,寫(xiě)了相關(guān)代碼如下:
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() # 定時(shí)任務(wù)啟動(dòng)
# 注冊(cè)多個(gè)藍(lán)圖模塊
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
那么再來(lái)看看藍(lán)圖模塊是怎么寫(xiě)的呢丰介?
目錄結(jié)構(gòu)是這個(gè)樣子的:
app
-admin
__init__.py
admin_manage.py
-flower
__init__.py
flower_manage.py
__init__.py
看下admin目錄下的兩個(gè)文件背蟆;flower目錄下也是類(lèi)似的
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
好啦,照葫蘆畫(huà)瓢哮幢,你也會(huì)注冊(cè)藍(lán)圖模塊了带膀!
三、理解Vue的單頁(yè)面應(yīng)用
很久以前用html做過(guò)網(wǎng)站橙垢,所以一直有一個(gè)慣性思維垛叨,覺(jué)得點(diǎn)擊網(wǎng)頁(yè)上的超鏈接跳來(lái)跳去的,就一定是在不同的頁(yè)面間切換柜某。
所以一開(kāi)始也按照這個(gè)思路來(lái)學(xué)習(xí)Vue嗽元,看到別人案例中,有一個(gè)個(gè)的Vue文件喂击,想當(dāng)然的就理解成對(duì)應(yīng)的頁(yè)面了剂癌,后來(lái)才發(fā)現(xiàn)并非如此。
組件
Vue中有一個(gè)很重要的概念就是組件翰绊!官網(wǎng)都是這么介紹組件的:
// 定義一個(gè)名為 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' })
組件是可復(fù)用的 Vue 實(shí)例佩谷,所以它們與 new Vue 接收相同的選項(xiàng),例如 data监嗜、computed谐檀、watch、methods 以及生命周期鉤子等裁奇。僅有的例外是像 el 這樣根實(shí)例特有的選項(xiàng)桐猬。
你可以將組件進(jìn)行任意次數(shù)的復(fù)用。
對(duì)了單頁(yè)面應(yīng)用簡(jiǎn)稱(chēng)SPA刽肠。羅里吧嗦一大堆课幕,那么它和單頁(yè)面應(yīng)用之間有什么聯(lián)系厦坛?
首先,我們定義組件乍惊;
然后杜秸,我們定義路由規(guī)則;
接著润绎,我們?cè)L問(wèn)路徑撬碟,就會(huì)跳轉(zhuǎn)到相應(yīng)的組件上去。
所以莉撇,我們并不是在頁(yè)面間跳轉(zhuǎn)呢蛤,而是通過(guò)路由規(guī)則,在組件之間進(jìn)行跳轉(zhuǎn)棍郎!
四其障、Vue-cli配置文件
在這個(gè)項(xiàng)目里,前端使用了Vue-cli腳手架涂佃。
如果你也直接用Vue-cli創(chuàng)建一個(gè)vue項(xiàng)目的話励翼,可以在config目錄下看到好幾個(gè).js文件,就是配置文件啦辜荠。
如果你是網(wǎng)上找來(lái)的項(xiàng)目汽抚,可能會(huì)略有不同,不過(guò)內(nèi)容上都是類(lèi)似的伯病。
這次我們就來(lái)看看index.js
文件里的部分內(nèi)容吧造烁,由于把配置詳解一遍很花時(shí)間,所以我們就聚焦一點(diǎn)午笛,來(lái)揭開(kāi)第一部分接口調(diào)用路徑的謎底惭蟋!
當(dāng)然,有些項(xiàng)目的配置不一定保持了初始的名字药磺,總之是含有module.exports = {
這行代碼的配置文件就對(duì)了敞葛!
我這里是這樣配置的:
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: {}
},
}
};
首先看下這個(gè)devServer.proxy.changeOrigin
,它是為了解決跨域問(wèn)題的一個(gè)設(shè)置与涡。
changeOrigin: true, //開(kāi)啟代理:在本地會(huì)創(chuàng)建一個(gè)虛擬服務(wù)端,然后發(fā)送請(qǐng)求的數(shù)據(jù)持偏,并同時(shí)接收請(qǐng)求的數(shù)據(jù)驼卖,這樣服務(wù)端和服務(wù)端進(jìn)行數(shù)據(jù)的交互就不會(huì)有跨域問(wèn)題
那什么是跨域贴谎?
瀏覽器從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源時(shí)阵翎,域名咬腋、端口窃植、協(xié)議任一不同,都是跨域榄审,如下例子:
域名:
主域名不同 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
協(xié)議:
http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
備注:
1、端口和協(xié)議的不同伊群,只能通過(guò)后臺(tái)來(lái)解決
2策精、localhost和127.0.0.1雖然都指向本機(jī)咽袜,但也屬于跨域
綜上询刹,我目前的設(shè)置前后端的端口不一樣范抓,也算是跨域了匕垫,因此就要設(shè)置成true啦!
接著看下這段:
proxy: {
'/api/': {
target: 'http://127.0.0.1:8089',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
vue-cli的這個(gè)設(shè)置來(lái)自于其使用的插件http-proxy-middleware寞秃,github地址如下:
https://github.com/chimurai/http-proxy-middleware
官網(wǎng)文檔中有這么一句:
proxy('/api', {...}) - matches paths starting with /api
這就說(shuō)明春寿,當(dāng)我們的路徑中含有/api
的時(shí)候忽孽,就會(huì)匹配到這里對(duì)應(yīng)的配置啦。
那么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') }
而我們的配置中有寫(xiě)pathRewrite: { '^/api': '' }
出革,因此proxy時(shí)要把路徑中的/api去掉骂束。
OK,那么整體來(lái)解釋一下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
過(guò)來(lái)的請(qǐng)求先紫,如果路徑中含有/api
遮精,就會(huì)被路由到127.0.0.1:8089
上去本冲,并且把路徑中的/api
去掉劫扒。
也就是說(shuō),當(dāng)我們?cè)谇岸嗽L問(wèn)了一個(gè)這樣的接口:
http://127.0.0.1:8010/api/flower/proGather/list
實(shí)際上對(duì)應(yīng)的是后端這個(gè)接口:
http://127.0.0.1:8089/flower/proGather/list
再來(lái)回到開(kāi)頭的問(wèn)題上來(lái)沟饥,為什么使用postman訪問(wèn)http://127.0.0.1:8089/api/flower/proGather/list
會(huì)報(bào)404的錯(cuò)誤添怔?這下你就清楚了吧贤旷!
那么再來(lái)問(wèn)一個(gè)問(wèn)題,后端的接口路徑為什么是這樣的呢幼驶,那就要來(lái)聊聊視圖函數(shù)啦艾杏。
五盅藻、路由與視圖函數(shù)
在《Flask Web開(kāi)發(fā)》一書(shū)中购桑,對(duì)于URL如何映射到Python函數(shù),有這樣的一段解釋?zhuān)?/p>
客戶端(例如Web瀏覽器)把請(qǐng)求發(fā)送給Web服務(wù)器氏淑,Web服務(wù)器再把請(qǐng)求發(fā)送給Flask程序?qū)嵗3绦驅(qū)嵗枰缹?duì)每個(gè)URL請(qǐng)求運(yùn)行哪些代碼,所以保存了一個(gè)URL到Python函數(shù)的映射關(guān)系守问。處理URL和函數(shù)之間關(guān)系的程序稱(chēng)為路由耗帕。
在Flask程序中定義路由的最簡(jiǎn)便方式仿便,是使用程序?qū)嵗峁┑腶pp.route修飾器体啰,把修飾的函數(shù)注冊(cè)為路由。
再來(lái)看我們的項(xiàng)目代碼闻坚。還記得剛才說(shuō)的目錄結(jié)構(gòu)么沽翔?
app
-admin
__init__.py
admin_manage.py
-flower
__init__.py
flower_manage.py
__init__.py
看一下flower_manage.py
這個(gè)文件:
from . import flower_module
@flower_module.route('/proGather/list')
def get_list():
pass
看來(lái)真正訪問(wèn)的是get_list()
這個(gè)函數(shù)呢!可是我們?cè)谛揎椘骼镏粚?xiě)了相對(duì)路徑/proGather/list
呀窿凤!
再來(lái)復(fù)習(xí)一下我們?cè)?code>create_app()函數(shù)里面注冊(cè)藍(lán)圖的時(shí)候是怎么寫(xiě)的:
from app.flower import flower_module
app.register_blueprint(flower_module, url_prefix='/flower')
所以flower_module
對(duì)應(yīng)著url_prefix='/flower'
仅偎,在拼上后端服務(wù)啟動(dòng)時(shí)的端口號(hào),可不就是http://127.0.0.1:8089/flower/proGather/list
了么雳殊!
六橘沥、小結(jié)
本文差不多就是這樣了,如果還覺(jué)得看得不夠清楚夯秃,下次我寫(xiě)一個(gè)示例項(xiàng)目傳到github上再解說(shuō)吧座咆。