[譯]什么是 web 框架稍刀?


本文系轉(zhuǎn)載于網(wǎng)絡(luò)。

Web 應(yīng)用框架校读,或者簡單的說是“Web 框架”,其實(shí)是建立 web 應(yīng)用的一種方式祖能。從簡單的博客系統(tǒng)到復(fù)雜的富 AJAX 應(yīng)用歉秫,web 上每個(gè)頁面都是通過寫代碼來生成的。我發(fā)現(xiàn)很多人都熱衷于學(xué)習(xí) web 框架技術(shù)养铸,例如 Flask 或這 Django 之類的雁芙,但是很多人并不理解什么是 web 框架,或者它們是如何工作的钞螟。這篇文章中兔甘,我將探索反復(fù)被忽略的 web 框架基礎(chǔ)的話題。閱讀完這篇文章鳞滨,你應(yīng)該首先對(duì)什么是 web 框架以及它們?yōu)槭裁磿?huì)存在有更深的認(rèn)識(shí)洞焙。這會(huì)讓你學(xué)習(xí)一個(gè)新的 web 框架變得簡單的多,還會(huì)讓你在使用不同的框架的時(shí)候做個(gè)明知的選擇太援。

Web 如何工作的闽晦?

在我們討論框架之前,我們需要理解 Web 如何“工作”的提岔。為此仙蛉,我們將深入挖掘你在瀏覽器里輸入一個(gè) URL 按下 Enter 之后都發(fā)生了什么。在你的瀏覽器中打開一個(gè)新的標(biāo)簽碱蒙,瀏覽http://www.jeffknupp.com我們討論為了顯示這個(gè)頁面荠瘪,瀏覽器都做了什么事情(不關(guān)心 DNS 查詢)。

Web 服務(wù)器

每個(gè)頁面都以 HTML 的形式傳送到你的瀏覽器中赛惩,HTML 是一種瀏覽器用來描述頁面內(nèi)容和結(jié)構(gòu)的語言哀墓。那些負(fù)責(zé)發(fā)送 HTML 到瀏覽器的應(yīng)用稱之為“Web 服務(wù)器”,會(huì)讓你迷惑的是喷兼,這些應(yīng)用運(yùn)行的機(jī)器通常也叫做 web 服務(wù)器篮绰。

然而,最重要的是要理解季惯,到最后所有的 web 應(yīng)用要做的事情就是發(fā)送 HTML 到瀏覽器吠各。不管應(yīng)用的邏輯多么復(fù)雜,最終的結(jié)果總是將 HTML 發(fā)送到瀏覽器(我故意將應(yīng)用可以響應(yīng)像 JSON或者 CSS等不同類型的數(shù)據(jù)忽略掉勉抓,因?yàn)樵诟拍钌鲜窍嗤模?/p>

web 應(yīng)用如何知道發(fā)送什么到瀏覽器呢贾漏?它發(fā)送瀏覽器請(qǐng)求的任何東西。

HTTP

瀏覽器從 web 服務(wù)器(或者叫應(yīng)用服務(wù)器)上使用 HTTP 協(xié)議下載網(wǎng)站藕筋,HTTP 協(xié)議是基于一種 請(qǐng)求-響應(yīng)(request-response)模型的纵散。客戶端(你的瀏覽器)從運(yùn)行在物理機(jī)器上的 web 應(yīng)用請(qǐng)求數(shù)據(jù),web 應(yīng)用反過來對(duì)你的瀏覽器請(qǐng)求進(jìn)行響應(yīng)伍掀。

重要的一點(diǎn)是掰茶,要記住通信總是由客戶端(你的瀏覽器)發(fā)起的,服務(wù)器(也就是 web 服務(wù)器)沒有辦法創(chuàng)建一個(gè)鏈接硕盹,發(fā)送沒有經(jīng)過請(qǐng)求的數(shù)據(jù)給你的瀏覽器符匾。如果你從 web 服務(wù)器上接收到數(shù)據(jù),一定是因?yàn)槟愕臑g覽器顯示地發(fā)送了請(qǐng)求瘩例。

HTTP Methods

在 HTTP 協(xié)議中,每條報(bào)文都關(guān)聯(lián)方法(method 或者 verb)甸各,不同的 HTTP 方法對(duì)應(yīng)客戶端可以發(fā)送的邏輯上不同類型的請(qǐng)求垛贤,反過來也代表了客戶端的不同意圖。例如趣倾,請(qǐng)求一個(gè) web 頁面的 HTML聘惦,與提交一個(gè)表單在邏輯上是不同的,所以這兩種行為就需要使用不同的方法儒恋。

HTTP GET

GET 方法就像其聽起來的那樣善绎,從 web 服務(wù)器上 get(請(qǐng)求)數(shù)據(jù)。GET 請(qǐng)求是到目前位置最常見的一種 HTTP 請(qǐng)求诫尽,在一次 GET 請(qǐng)求過程中禀酱,web 應(yīng)用對(duì)請(qǐng)求頁面的 HTML 進(jìn)行響應(yīng)之外,就不需要做任何事情了牧嫉。特別的剂跟,web 應(yīng)用在 GET 請(qǐng)求的結(jié)果中,不應(yīng)該改變應(yīng)用的狀態(tài)(比如酣藻,不能基于 GET 請(qǐng)求創(chuàng)建一個(gè)新帳號(hào))曹洽。正是因?yàn)檫@個(gè)原因,GET 請(qǐng)求通常認(rèn)為是“安全”的辽剧,因?yàn)樗麄儾粫?huì)導(dǎo)致應(yīng)用的改變送淆。

HTTP POST

顯然,除了簡單的查看頁面之外怕轿,應(yīng)該還有更多與網(wǎng)站進(jìn)行交互的操作偷崩。我們也能夠向應(yīng)用發(fā)送數(shù)據(jù),例如通過表單撤卢。為了達(dá)到這樣的目的环凿,就需要一種不同類型的請(qǐng)求方法:POST。POST 請(qǐng)求通常攜帶由用戶輸入的數(shù)據(jù)放吩,web 應(yīng)用收到之后會(huì)產(chǎn)生一些行為智听。通過在表單里輸入你的信息登錄一個(gè)網(wǎng)站,就是 POST 表單的數(shù)據(jù)給 web 應(yīng)用的。

不同于 GET 請(qǐng)求到推,POST 請(qǐng)求通常會(huì)導(dǎo)致應(yīng)用狀態(tài)的改變考赛。在我們的例子中,當(dāng)表單 POST 之后莉测,一個(gè)新的賬戶被創(chuàng)建颜骤。不同于 GET 請(qǐng)求,POST 請(qǐng)求不總是生成一個(gè)新的 HTML 頁面發(fā)送到客戶端捣卤,而是客戶端使用響應(yīng)的響應(yīng)碼(response code

)來決定對(duì)應(yīng)用的操作是否成功忍抽。

HTTP Response Codes

通常來說,web 服務(wù)器返回 200 的響應(yīng)碼董朝,意思是鸠项,“我已經(jīng)完成了你要求我做的事情,一切都正匙咏”祟绊。響應(yīng)碼總是一個(gè)三位數(shù)字的代號(hào),web 應(yīng)用在每個(gè)響應(yīng)的同時(shí)都發(fā)送一個(gè)這樣的代號(hào)哥捕,表明給定的請(qǐng)求的結(jié)果牧抽。響應(yīng)碼 200 字面意思是“OK”,是響應(yīng)一個(gè) GET 請(qǐng)求大多情況下都使用的代號(hào)遥赚。然而對(duì)于 POST 請(qǐng)求扬舒, 可能會(huì)有 204(“No Content”)發(fā)送回來,意思是“一切都正常鸽捻,但是我不準(zhǔn)備向你顯示任何東西”呼巴。

POST 請(qǐng)求仍然會(huì)發(fā)送一個(gè)特殊的 URL,這個(gè) URL 可能和提交數(shù)據(jù)的頁面不同御蒲,意識(shí)這一點(diǎn)是至關(guān)重要的衣赶。還是以我們的登錄為例,表單可能是在 www.foo.com/signup

頁面厚满,然而點(diǎn)擊 submit

府瞄,可能會(huì)導(dǎo)致帶有表單數(shù)據(jù)的 POST 請(qǐng)求發(fā)送到 www.foo.com/process_sigup

上。POST 請(qǐng)求要發(fā)送的位置在表單的 HTML 中有特別標(biāo)明碘箍。

Web 應(yīng)用

你可以僅僅使用 HTTP GET 和 POST 做很多事情遵馆。一個(gè)應(yīng)用程序負(fù)責(zé)去接收一個(gè) HTTP 請(qǐng)求,同時(shí)給以 HTTP 響應(yīng)丰榴,通常包含了請(qǐng)求頁面的 HTML货邓。POST 請(qǐng)求會(huì)引起 web 應(yīng)用做出一些行為,可能是往數(shù)據(jù)庫中添加一條記錄這樣的四濒。還有很多其它的 HTTP 方法换况,但是我們目前只關(guān)注 GET 和 POST职辨。

那么最簡單的 web 應(yīng)用是什么樣的呢?我們可以寫一個(gè)應(yīng)用戈二,讓它一直監(jiān)聽 80 端口(著名的 HTTP 端口舒裤,幾乎所有 HTTP 都發(fā)送到這個(gè)端口上)。一旦它接收到等待的客戶端發(fā)送的請(qǐng)求連接觉吭,然后它就會(huì)回復(fù)一些簡單的 HTML腾供。

下面是程序的代碼:

import socketHOST = ''PORT = 80listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.bind((HOST, PORT))listen_socket.listen(1)connection, address = listen_socket.accept()request = connection.recv(1024)connection.sendall(b"""HTTP/1.1 200 OKContent-type: text/html

Hello, World!

""")connection.close()

(如果上面的代碼不工作,試著將 PORT 改為類似 8080 這樣的端口鲜滩。)

這個(gè)代碼接收簡單的鏈接和簡單的請(qǐng)求伴鳖,不管請(qǐng)求的 URL 是什么,它都會(huì)響應(yīng) HTTP 200(所以绒北,這不是一個(gè)真正意義上的 web 服務(wù)器)黎侈。Content-type:text/html

行代碼的是 header 字段,header 用來提供請(qǐng)求或者響應(yīng)的元信息闷游。這樣,我們就告訴了客戶端接下來的數(shù)據(jù)是 HTML贴汪。

請(qǐng)求的剖析

如果看一下測試上面程序的 HTTP 請(qǐng)求脐往,你會(huì)發(fā)現(xiàn)它和 HTTP 響應(yīng)非常類似。第一行

扳埂,在這個(gè)例子中是 GET / HTTP/1.0

业簿。第一行之后就是一些類似Accept:/

這樣的頭(意思是我們希望在響應(yīng)中接收任何內(nèi)容)。

我們響應(yīng)和請(qǐng)求有著類似的第一行阳懂,格式是

梅尤,在外面的例子中是HTTP/1.1 200 OK

。接下來是頭部岩调,與請(qǐng)求的頭部有著相同的格式巷燥。最后是響應(yīng)的實(shí)際包含的內(nèi)容。注意号枕,這會(huì)被解釋為一個(gè)字符串或者二進(jìn)制文件缰揪,Content-type頭告訴客戶端怎樣去解釋響應(yīng)。

Web 服務(wù)器之殤

如果我們繼續(xù)以上面的例子為基礎(chǔ)建立 web 應(yīng)用葱淳,我們還需要解決很多問題:

我們怎樣檢測請(qǐng)求的 URL 以及返回正確的頁面钝腺?

除了簡單的 GET 請(qǐng)求之外我們?nèi)绾翁幚?POST 請(qǐng)求?

我們?nèi)绾卫斫飧呒?jí)的概念赞厕,如 session 和 cookie艳狐?

我們?nèi)绾螖U(kuò)展程序以使其處理上千個(gè)并發(fā)連接?

就像你想的那樣皿桑,沒有人愿意每次建立一個(gè) web 應(yīng)用都要解決這些問題毫目。正是這個(gè)原因蔬啡,就有處理 HTTP 協(xié)議本身和有效解決上面問題的辦法的包存在。然而蒜茴,記住了星爪,它們的核心功能和我們的例子是相同的:監(jiān)聽請(qǐng)求,帶有一些 HTML 發(fā)回 HTTP 響應(yīng)粉私。

解決兩大問題:路由和模板

圍繞建立 web 應(yīng)用的所有問題中顽腾,兩個(gè)問題尤其突出:

我們?nèi)绾螌⒄?qǐng)求的 URL 映射到處理它的代碼上?

我們怎樣動(dòng)態(tài)地構(gòu)造請(qǐng)求的 HTML 返回給客戶端诺核,HTML 中帶有計(jì)算得到的值或者從數(shù)據(jù)庫中取出來的信息抄肖?

每個(gè) web 框架都以某種方法來解決這些問題,也有很多不同的解決方案窖杀。用例子來說明更容易理解漓摩,所以我將針對(duì)這些問題討論 Django 和 Flask 的解決方案。但是入客,首先我們還需要簡單討論一下 MVC 管毙。

Django 中的 MVC

Django 充分利用 MVC 設(shè)計(jì)模式。 MVC桌硫,也就是Model-View-Controller (模型-視圖-控制器)夭咬,是一種將應(yīng)用的不同功能從邏輯上劃分開。models 代表的是類似數(shù)據(jù)庫表的資源(與 Python 中用class來對(duì)真實(shí)世界目標(biāo)建模使用的方法大體相同)铆隘。controls 包括應(yīng)用的業(yè)務(wù)邏輯卓舵,對(duì) models 進(jìn)行操作。為了動(dòng)態(tài)生成代表頁面的 HTML膀钠,需要 views 給出所有要?jiǎng)討B(tài)生成頁面的 HTML 的信息掏湾。

在 Django 中有點(diǎn)讓人困惑的是,controllers 被稱做 views肿嘲,而 views 被稱為 templates融击。除了名字上的有點(diǎn)奇怪,Django 很好地實(shí)現(xiàn)了 MVC 的體系架構(gòu)睦刃。

Django 中的路由

路由是處理請(qǐng)求 URL 到負(fù)責(zé)生成相關(guān)的 HTML 的代碼之間映射的過程砚嘴。在簡單的情形下,所有的請(qǐng)求都是有相同的代碼來處理(就像我們之前的例子那樣)涩拙。變得稍微復(fù)雜一點(diǎn)际长,每個(gè) URL 對(duì)應(yīng)一個(gè) view function 。舉例來說兴泥,如果請(qǐng)求 www.foo.com/bar

這樣的 URL工育,調(diào)用 handler_bar()

這樣的函數(shù)來產(chǎn)生響應(yīng)。我們可以建立這樣的映射表搓彻,枚舉出我們應(yīng)用支持的所有 URL 與它們相關(guān)的函數(shù)如绸。

然而嘱朽,當(dāng) URL 中包含有用的數(shù)據(jù),例如資源的 ID(像這樣 www.foo.com/users/3/

) 怔接,那么這種方法將變得非常臃腫搪泳。我們?nèi)绾螌?URL 映射到一個(gè) view 函數(shù),同時(shí)如何利用我們想顯示 ID 為 3 的用戶扼脐?

Django 的答案是岸军,將 URL 正則表達(dá)式映射到可以帶參數(shù)的 view 函數(shù)。例如瓦侮,我假設(shè)匹配^/users/(?P\d+)/$

的 URL 調(diào)用 display_user(id)

這樣的函數(shù)艰赞,這兒參數(shù) id 是正則表達(dá)式中匹配的 id。這種方法肚吏,任何 /users//

這樣的 URL 都會(huì)映射到 display_user

函數(shù)方妖。這些正則表達(dá)式可以非常復(fù)雜,包含關(guān)鍵字和參數(shù)罚攀。

Flask 中的路由

Flask 采取了一點(diǎn)不同的方法党觅。將一個(gè)函數(shù)和請(qǐng)求的 URL 關(guān)聯(lián)起來的標(biāo)準(zhǔn)方法是通過使用 route()

裝飾器。下面是 Flask 代碼斋泄,在功能上和上面正則表達(dá)式方法相同:

@app.route('/users//')def display_user(id): # ...

就像你看到的這樣仔役,裝飾器使用幾乎最簡單的正則表達(dá)式的形式來將 URL 映射到參數(shù)。通過傳遞給route()

的 URL 中包含的

指令是己,可以提取到參數(shù)。路由像 /info/about_us.html

這樣的靜態(tài) URL任柜,可以像你預(yù)想的這樣 @app.route('/info/about_us.html')

處理卒废。

通過 Templates 產(chǎn)生 HTML

繼續(xù)上面的例子,一旦我們有合適的代碼映射到正確的 URL宙地,我們?nèi)绾蝿?dòng)態(tài)生成 HTML摔认?對(duì)于 Django 和 Flask,答案都是通過HTML Templating宅粥。

HTML Templating 和使用 str.format()

類似:需要?jiǎng)討B(tài)輸出值的地方使用占位符填充参袱,這些占位符后來通過 str.format()

函數(shù)用參數(shù)替換掉。想象一下秽梅,整個(gè) web 頁面就是一個(gè)字符串抹蚀,用括號(hào)標(biāo)明動(dòng)態(tài)數(shù)據(jù)的位置,最后再調(diào)用 str.format()

企垦。Django 模板和 Flask 使用的模板引擎 Jinja2 都使用的是這種方法环壤。

然而,不是所有的模板引擎都能相同的功能钞诡。Django 支持在模板里基本的編程郑现,而 Jinja2 只能讓你執(zhí)行特定的代碼(不是真正意義上的代碼湃崩,但也差不多)。Jinja2 可以緩存渲染之后的模板接箫,讓接下來具有相同參數(shù)的請(qǐng)求可以直接從緩存中返回結(jié)果攒读,而不是用再次花大力氣渲染。

數(shù)據(jù)庫交互

Django 有著“功能齊全”的設(shè)計(jì)哲學(xué)辛友,其中包含了一個(gè) ORM(ObjectRealationalMapper薄扁,對(duì)象關(guān)系映射),ORM 的目的有兩方面:一是將 Python 的 class 與數(shù)據(jù)庫表建立映射瞎领,而是剝離出不同數(shù)據(jù)庫引擎直接的差異泌辫。沒人喜歡 ORM,因?yàn)樵诓煌挠蛑g映射永遠(yuǎn)不完美九默,然而這還在承受范圍之內(nèi)震放。Django 是功能齊全的,而 Flask 是一個(gè)微框架驼修,不包括 ORM殿遂,盡管它對(duì) SQLAlchemy 兼容性非常好,SQLAlchemy 是 Django ORM 的最大也是唯一的競爭對(duì)手乙各。

內(nèi)嵌 ORM 讓 Django 有能力創(chuàng)建一個(gè)功能豐富的 CRUD 應(yīng)用墨礁,從服務(wù)器端角度來看,CRUDCreateReadUpdateDelete)應(yīng)用非常適合使用 web 框架技術(shù)耳峦。Django 和 Flask-SQLchemy 可以直接對(duì)每個(gè) model 進(jìn)行不同的 CRUD 操作恩静。

再談 web 框架

到現(xiàn)在為止,web 框架的目的應(yīng)該非常清晰了:向程序員隱藏了處理 HTTP 請(qǐng)求和響應(yīng)相關(guān)的基礎(chǔ)代碼蹲坷。至于隱藏多少這取決于不同的框架驶乾,Django 和 Flask 走向了兩個(gè)極端:Django 包括了每種情形,幾乎成了它致命的一點(diǎn)循签;Flask 立足于“微框架”级乐,僅僅實(shí)現(xiàn) web 應(yīng)用需要的最小功能,其它的不常用的 web 框架任務(wù)交由第三方庫來完成县匠。

但是最后要記住的是风科,Python web 框架都以相同的方式工作的:它們接收 HTTP 請(qǐng)求,分派代碼乞旦,產(chǎn)生 HTML贼穆,創(chuàng)建帶有內(nèi)容的 HTTP 響應(yīng)。事實(shí)上杆查,所有主流的服務(wù)器端框架都以這種方式工作的( JavaScript 框架除外)扮惦。但愿了解了這些框架的目的,你能夠在不同的框架之間選擇適合你應(yīng)用的框架進(jìn)行開發(fā)亲桦。


英文原版

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末崖蜜,一起剝皮案震驚了整個(gè)濱河市浊仆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豫领,老刑警劉巖抡柿,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異等恐,居然都是意外死亡洲劣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門课蔬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囱稽,“玉大人,你說我怎么就攤上這事二跋≌骄” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵扎即,是天一觀的道長吞获。 經(jīng)常有香客問我,道長谚鄙,這世上最難降的妖魔是什么各拷? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮闷营,結(jié)果婚禮上烤黍,老公的妹妹穿的比我還像新娘。我一直安慰自己傻盟,他們只是感情好蚊荣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著莫杈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奢入。 梳的紋絲不亂的頭發(fā)上筝闹,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音腥光,去河邊找鬼关顷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛武福,可吹牛的內(nèi)容都是我干的议双。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捉片,長吁一口氣:“原來是場噩夢啊……” “哼平痰!你這毒婦竟也來了汞舱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤宗雇,失蹤者是張志新(化名)和其女友劉穎昂芜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赔蒲,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泌神,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舞虱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欢际。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矾兜,靈堂內(nèi)的尸體忽然破棺而出损趋,到底是詐尸還是另有隱情,我是刑警寧澤焕刮,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布舶沿,位于F島的核電站,受9級(jí)特大地震影響配并,放射性物質(zhì)發(fā)生泄漏括荡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一溉旋、第九天 我趴在偏房一處隱蔽的房頂上張望畸冲。 院中可真熱鬧,春花似錦观腊、人聲如沸邑闲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苫耸。三九已至,卻和暖如春儡陨,著一層夾襖步出監(jiān)牢的瞬間褪子,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工骗村, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫌褪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓胚股,卻偏偏與公主長得像笼痛,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文系轉(zhuǎn)載于網(wǎng)絡(luò)。 Web 應(yīng)用框架倘核,或者簡單的說是“Web 框架”泣侮,其實(shí)是建立 web 應(yīng)用的一種方式。從簡單的...
    everfight閱讀 292評(píng)論 0 3
  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停紧唱,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,184評(píng)論 22 257
  • 可不要被名字迷惑漏益,它可不是web網(wǎng)頁的框架蛹锰,而是服務(wù)器用來產(chǎn)生web網(wǎng)頁時(shí)用到的工具。 Web應(yīng)用框架(簡稱Web...
    約伯閱讀 3,762評(píng)論 0 3
  • 我們常用的web前端框架其實(shí)簡單稱呼叫web框架,現(xiàn)階段web前端技術(shù)成熟轻庆,從視覺體驗(yàn)到用戶體驗(yàn)都是比較好的癣猾,這也...
    UIleader閱讀 1,685評(píng)論 0 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)余爆,斷路器纷宇,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139