隨着前端NodeJs技術的火爆,如今的前端已經非之前傳統意義上的前端了,各類前端框架(Vue、React、Angular......)井噴式發展,配合NodeJs服務端渲染引擎,目前前端能完成的工做不只僅侷限於CSS,JS等方面,不少系統的業務邏輯均可以放在前端來完成,例如我司的管控html
那可能有些人會說,前端這麼火,NodeJs發展這麼迅猛,後端是否是之後都沒事情幹了,其實否則,拿Java來講,通過這麼多年發展,已經至關穩定,完善的生態圈也非最近今年發展起來的NodeJs可比,咱們經常說聞道有前後,術業有專攻,用在這裏最合適不過了,集羣、分佈式、高可用等等技術仍是須要後端架構師來思考的事情前端
目前前端同後端的合做方式是先後端分離,經過Nginx+Tomcat的組合部署(還可加nodejs中間件)方式能有效的進行解耦,而且先後端分離爲項目之後的架構擴展、微服務化、組件化都打下重要基礎,因此這在之後是一個發展的必然趨勢,咱們須要去適應,作出改變!!!java
早期的開發方式以下圖:node
這也是我前面工做1-3年的開發方式,咱們沒有前端幫咱們寫JS函數功能,全部的頁面表單驗證,數據渲染,數據接口編寫都是咱們後端所有實現,看上去更像是一個全棧工程師,從需求分析、搭建整個技術架構、數據庫表設計、功能設計、編碼開發,再到最終部署上線,咱們無所不在,這可能也是目前不少小公司仍然在沿用的開發方式,不少後端同窗擔負起了項目的方方面面linux
以我目前的經驗來看,這樣的開發方式對我我的的成長是有益無害的,由於你只有在瞭解了前端的JS/CSS/HTML的狀況下,而後再談目前的先後端分離,會讓你的工做事半功倍,在寫後端接口前,你腦子裏浮現的是整個功能的交互頁面,最終呈現的是先後端合做開發好後的的終端結果,這大大縮減了先後端的溝通交流nginx
可能因爲我在前面三年積累了豐富的前端經驗,在上家公司主要負責開發官網、微信、後臺等相關係統的接口,前期咱們的開發方式雖然也是先後端分離的方式,但大都使用jsonp跨域接口調用的方式來達到分離效果,後端全部的接口都是可跨域調用的jsonp形式,拋開須要登陸的受權以外的接口,前端在開發的時候本地無需開啓服務便可調用服務端接口,而後渲染數據,完成頁面交互渲染效果git
jsonp的優勢web
不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制ajax
兼容性更好,在更低版本的ie瀏覽器中都能兼容,這裏區別於cors跨域類型redis
jsonp的原理其實很簡單,固然,這也涉及到前端的知識,簡單點說就是js端的function函數執行
正常的後端響應數據,例如:
{ "code":"8200", "data":{ "id":"100", "name":"Test" //more...... } }
jsonp須要的返回格式:
callback({ "code":"8200", "data":{ "id":"100", "name":"Test" //more...... } });
前端在頁面定義callback回調函數,callback函數接收後端響應回來的data-json數據,後端響應後執行callback函數達到調用前端業務邏輯的目的,渲染頁面
這種配合開發方式也是適合前端尚未引入Node等一站式開發解決方案的狀況下引入的,純粹的HTML+CSS+JS同後端對接,綁定業務接口,渲染數據
咱們在使用JSONP開發的時候,前端都是須要在頁面端寫死HOST+IP接口地址,存在很重大一個弊端就是前端須要些config文件,來配置咱們後端的接口請求地址,若是前端工程師規範意識強一點,會通用到一個配置文件裏,可是若是沒有這方面的意識的話,就會出現代碼裏硬編碼的狀況,不利於服務器遷移,代碼更新,接口變更等操做
爲規避上面碰到的問題,使用nginx的反向代理功能,將後端服務器代理下來,前端在開發的時候本地開啓nginx服務,即解決了jsonp跨域問題,同時也解決了無需寫死後端的服務ip+端口地址,利於後端在部署時整合代碼,減小沒必要要的錯誤
隨着NodeJs的火熱,前端已經能夠本地開啓服務寫接口的狀況下,就相似服務端開啓tomcat同樣,在這樣的狀況下,前端框架VUE、React等都在此基礎上,提供了一套完整的技術解決方案,這和上面說到的開啓nginx服務架構有點相似
這樣作的意義:真正的解放了先後端,專一各自擅長的領域
技術架構以下:
前端node服務直接訪問後端Java Restful Api接口服務,Api接口最終訪問數據庫完成數據查詢最終返回node層,node渲染響應數據到前端
若是存在會話信息同步等問題,可使用中間件,例如redis緩存數據庫,解決前端node和後端Api信息同步問題,傳參能夠經過JWT等方式完成接口權限驗證
無論是jsonp仍是ajax+nginx這兩種方式,node做爲中間件均可以輕鬆切換處理,並且node做爲中間層,還能夠將多個後端接口組合成一整個數據集,最終以同步的方式渲染前端,這也利於作SEO優化,也是前面兩種方式沒法作到的
關於先後端分離,詳細可閱讀先後端分離的思考與實踐,該文章詳細的列述了關於先後端分離的實際經驗
隨着先後端的分離,後端工程師不須要編寫頁面,不須要寫JS,只須要提供接口便可,但是就是僅僅這一個接口,對於不少後端開發工程師而言,在實際開發,同前端對接的過程當中,依然問題重重
不少後端同窗說我只負責寫接口,其餘我一律無論,這樣形成的後果就是
一、接口結構無序、雜亂無章
二、接口和實際業務場景不相匹配、不可用
三、頻繁的同前端溝通,簡單的事情複雜化,先後端都很惱火
四、事情沒作好
後端在編寫接口前,首先是對業務的理解,在對業務未理解透徹以前,編碼都是無心義的,做爲後端來講,須要鍛鍊本身對整個系統全局考慮的能力,接口之間並不是是毫無關聯的,咱們在寫第一個接口之間,其餘接口之間的業務邏輯也許考慮到,這在後端團隊合做開發不一樣功能的狀況下顯得尤其重要.
後端在開發接口時,我以爲主要從如下幾個方面須要注意:
接口url 定義
接口類型、參數
全局錯誤碼定義
接口json格式
接口文檔編寫
對於後端開發人員來講,接口前端入參,最終組合查詢數據庫資源,通過一系列相關業務場景下的計算,響應給前端json數據,每一層url的path定義須要清晰明瞭,這和後端在使用AOP定義事務管理同理,後端service須要知足必定的命名規範,這樣方便統一管理,並且有這層規範後,後續的先後端對接會輕鬆不少
爲了在許多API和長時間內提供一致的開發人員體驗,API使用的全部名稱應爲:
這包括接口,資源,集合,方法和消息的名稱。
因爲許多開發人員不是英文母語人士,所以這些命名約定的目標之一是確保大多數開發人員可以輕鬆瞭解API。 它經過鼓勵在命名方法和資源時使用簡單,一致和小的詞彙表來實現。
關於接口的請求類型,目前比較經常使用的:GET
、POST
、PUT
、DELETE
、PATCH
GET(SELECT):從服務器取出資源(一項或多項)。
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE):在服務器更新資源(客戶端提供改變後的完整資源)。
PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
DELETE(DELETE):從服務器刪除資源。
後端可根據不一樣的業務場景定義不一樣的接口類型
在定義接口參數之時,目前咱們經常使用的幾種提交方式
表單提交,application/x-www-form-urlencoded
表單提交主要針對key-value
的提交形式
以下Java片斷:
@PostMapping("/queryAll") public RestfulMessage queryAll(RuleCheckLogs ruleCheckLogs, @RequestParam(value = "current_page",defaultValue = "1")Integer current_page , @RequestParam(value = "page_size",defaultValue = "10")Integer page_size , @RequestParam(value = "tableName",required = false) String tableName){ RestfulMessage restfulMessage=new RestfulMessage(); try{ assertArgumentNotEmpty(ruleCheckLogs.getProjectId(),"質檢方案id不能爲空"); restfulMessage.setData(qcRuleCheckLogsService.queryRuleLogsByPage(ruleCheckLogs,tableName,current_page,page_size)); }catch (Exception e){ restfulMessage=wrapperException(e); } return restfulMessage; }
文件流提交
json提交,application/json
json提交方式在SpringMVC或Spring Boot中主要有兩種,一種是以@RequestBody
註解接收方式,另一種是以HttpEntity<String> requestEntity
字節接收
Java代碼示例:
@PostMapping("/mergeModelEntitys") public RestfulMessage mergeModelEntitys(HttpEntity<String> requestEntity){ RestfulMessage restfulMessage=new RestfulMessage(); try{ JsonObject paramsJson = paramJson(requestEntity); assertJsonNotEmpty(paramsJson,"請求參數不能爲空"); //more... }catch (Exception e){ restfulMessage=wrapperException(e); } return restfulMessage; }
錯誤碼的定義同HTTP請求狀態碼同樣,對接者能經過系統定義的錯誤碼,快速瞭解接口返回錯誤信息,方便排查錯誤緣由
{ "code": "8200", "message": "Success", "data": { "total_page": 1, "current_page": 1, "page_size": 10, "count": 5, "data": [ { "id": "a29ab07f1d374c22a72e884d4e822b29", //...... }//.... ] } }
後端響應json給前端須要注意如下幾點:
一、json格式需固定
例如以下圖形
如上圖所示,橫向是時間,縱向是value值
咱們給出的json結構應該如此:
[ { "date":"2018-01", "value":100 }, { "date":"2018-02", "value":200 } //more... ]
在工做中,咱們常常遇見這樣的數據格式:
[ "2018-01":{ value:100 }, "2018-02":{ value:200 } //more... ]
這裏所說的json格式固定主要針對此種狀況,後端給到前端的接口格式必須是固定的,全部動態數據值都需相應的key與之對應
二、全部返回接口數據需直接可用,越簡單越好
後端提供給前端的接口數據,最終交給前端的工做,只須要讓前端渲染數據便可,越簡單越好,不因摻雜過多的業務邏輯讓前端處理,全部複雜的業務邏輯,能合併規避掉的都需後端處理掉.
接口文檔編寫是先後端對接重要依據,後端寫明接口文檔,前端根據接口文檔對接
文檔形勢目前主要分幾種:
一、依賴swagger框架,自動生成接口文檔(swagger只能生成基於key-value詳細參數方式,針對json格式,沒法說明具體請求內容)
二、手動編寫說明文檔,推薦markdown編寫
萬事俱備,只欠東風,雖然上面咱們準備了全部咱們該準備的,接口定義天衣無縫,接口文檔也已說明,但在對接時任然可能出現問題,此時我想咱們還需注意的細節
一、後端接口需自行進行Junit單元測試
Spring目前集成Junit框架可方便進行單元測試,包括對業務bean的方法測試,以及針對api的mock測試
@RunWith(SpringRunner.class) @SpringBootTest public class QcWebApplicationTests { @Autowired private WebApplicationContext context; private MockMvc mvc; @Autowired QcFieldService qcFieldService; @Before public void setUp() throws Exception { //初始化mock對象 mvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void queryByDsId(){ try { //針對mock-接口Controller層測試 mvc.perform(MockMvcRequestBuilders.post("/qc/entity/queryByDsId") .contentType(MediaType.APPLICATION_JSON_UTF8) .param("dsId", "7d4c101498c742368ef7232f492b95bc") .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } catch (Exception e) { e.printStackTrace(); } } @Test public void testUpdateField(){ QcField qcField=new QcField(); qcField.setId("513ee55f5dc2498cb69b14b558bc73e6"); qcField.setShortName("密碼"); //業務bean-service方法測試 qcFieldService.updateBatchFields(Lists.newArrayList(qcField)); }
二、使用工具測試,推薦PostMan
做爲接口調試神器,Postman大名想必你們都已知道
做爲後端來講,咱們須要學會查看chrome推薦給咱們的審查元素的功能,可參看Chrome開發工具介紹
chrome提供了一個能夠copy當前接口的url功能,最終生成curl命令行
最終經過Copy as cURL(bash)
功能可生成curl命令
curl 'http://demo.com/qc/ds/getAll' -H 'Origin: http://demo.com' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: zh-CN,zh;q=0.9' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json, text/plain, */*' -H 'Referer: http://demo.com/index.html' -H 'Connection: keep-alive' --data 'current_page=1&page_size=6&' --compressed
以上命令能夠在Linux等各終端直接執行
curl命令是一個利用URL規則在命令行下工做的文件傳輸工具。它支持文件的上傳和下載,因此是綜合傳輸工具,但按傳統,習慣稱curl爲下載工具。做爲一款強力工具,curl支持包括HTTP、HTTPS、ftp等衆多協議,還支持POST、cookies、認證、從指定偏移處下載部分文件、用戶代理字符串、限速、文件大小、進度條等特徵。作網頁處理流程和數據檢索自動化,curl能夠祝一臂之力。
postman提供導入curl命令行
三、先後端需心平氣和溝通,勿推卸責任,先後端開發人員水平不盡相同,做爲同事,須要的是團結合做,努力將事情作好,而非相互推卸
先後端分離,簡化了咱們的開發方式,不一樣人專一於不一樣的領域,技術價值最大化,大大提升工做效率,咱們在掌握這些技能的同時,也須要增強自身的發展,以適應當前的技術發展趨勢,無論是前端仍是後端,多瞭解一些,老是沒錯的,古人云:技多不壓身,我想也正是此理!!!
最後,推薦一個Swagger的Ui工具swagger-bootstrap-ui項目,Java開發者若是使用swagger的話能夠直接使用,很方便哦,先後端接口對接更加方便、清晰.快試試吧