前後端對接的思考及總結

說在前面的話

隨着前端NodeJs技術的火爆,現在的前端已經非以前傳統意義上的前端了,各種前端框架(Vue、React、Angular......)井噴式發展,配合NodeJs服務端渲染引擎,目前前端能完成的工作不僅僅侷限於CSS,JS等方面,很多系統的業務邏輯都可以放在前端來完成,例如我司的管控

那可能有些人會說,前端這麼火,NodeJs發展這麼迅猛,後端是不是以後都沒事情幹了,其實不然,拿Java來說,經過這麼多年發展,已經相當穩定,完善的生態圈也非最近今年發展起來的NodeJs可比,我們常常說聞道有先後,術業有專攻,用在這裏最合適不過了,集羣分佈式高可用等等技術還是需要後端架構師來思考的事情

目前前端同後端的合作方式是前後端分離,通過Nginx+Tomcat的組合部署(還可加nodejs中間件)方式能有效的進行解耦,並且前後端分離爲項目以後的架構擴展、微服務化、組件化都打下重要基礎,所以這在以後是一個發展的必然趨勢,我們需要去適應,做出改變!!!

早期的開發方式

早期的開發方式如下圖:

這也是我前面工作1-3年的開發方式,我們沒有前端幫我們寫JS函數功能,所有的頁面表單驗證,數據渲染,數據接口編寫都是我們後端全部實現,看上去更像是一個全棧工程師,從需求分析、搭建整個技術架構、數據庫表設計、功能設計、編碼開發,再到最終部署上線,我們無所不在,這可能也是目前很多小公司仍然在沿用的開發方式,很多後端同學擔負起了項目的方方面面

以我目前的經驗來看,這樣的開發方式對我個人的成長是有益無害的,因爲你只有在瞭解了前端的JS/CSS/HTML的情況下,然後再談目前的前後端分離,會讓你的工作事半功倍,在寫後端接口前,你腦子裏浮現的是整個功能的交互頁面,最終呈現的是前後端合作開發好後的的終端結果,這大大縮減了前後端的溝通交流

前後端分離的探索

jsonp

可能由於我在前面三年積累了豐富的前端經驗,在上家公司主要負責開發官網、微信、後臺等相關係統的接口,前期我們的開發方式雖然也是前後端分離的方式,但大都使用jsonp跨域接口調用的方式來達到分離效果,後端所有的接口都是可跨域調用的jsonp形式,拋開需要登錄的授權之外的接口,前端在開發的時候本地無需開啓服務即可調用服務端接口,然後渲染數據,完成頁面交互渲染效果

jsonp的優點

不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制

兼容性更好,在更低版本的ie瀏覽器中都能兼容,這裏區別於cors跨域類型

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函數達到調用前端業務邏輯的目的,渲染頁面

nginx+ajax

這種配合開發方式也是適合前端還沒有引入Node等一站式開發解決方案的情況下引入的,純粹的HTML+CSS+JS同後端對接,綁定業務接口,渲染數據

我們在使用JSONP開發的時候,前端都是需要在頁面端寫死HOST+IP接口地址,存在很重大一個弊端就是前端需要些config文件,來配置我們後端的接口請求地址,如果前端工程師規範意識強一點,會通用到一個配置文件裏,但是如果沒有這方面的意識的話,就會出現代碼裏硬編碼的情況,不利於服務器遷移,代碼更新,接口變動等操作

爲規避上面碰到的問題,使用nginx的反向代理功能,將後端服務器代理下來,前端在開發的時候本地開啓nginx服務,即解決了jsonp跨域問題,同時也解決了無需寫死後端的服務ip+端口地址,利於後端在部署時整合代碼,減少不必要的錯誤

node

隨着NodeJs的火熱,前端已經可以本地開啓服務寫接口的情況下,就類似服務端開啓tomcat一樣,在這樣的情況下,前端框架VUE、React等都在此基礎上,提供了一套完整的技術解決方案,這和上面說到的開啓nginx服務架構有點類似

這樣做的意義:真正的解放了前後端,專注各自擅長的領域

技術架構如下:

前端node服務直接訪問後端Java Restful Api接口服務,Api接口最終訪問數據庫完成數據查詢最終返回node層,node渲染響應數據到前端

如果存在會話信息同步等問題,可以使用中間件,例如redis緩存數據庫,解決前端node和後端Api信息同步問題,傳參可以通過JWT等方式完成接口權限驗證

不管是jsonp還是ajax+nginx這兩種方式,node作爲中間件都可以輕鬆切換處理,而且node作爲中間層,還可以將多個後端接口組合成一整個數據集,最終以同步的方式渲染前端,這也利於做SEO優化,也是前面兩種方式無法做到的

關於前後端分離,詳細可閱讀前後端分離的思考與實踐,該文章詳細的列述了關於前後端分離的實際經驗

談談接口

隨着前後端的分離,後端工程師不需要編寫頁面,不需要寫JS,只需要提供接口即可,可是就是僅僅這一個接口,對於很多後端開發工程師而言,在實際開發,同前端對接的過程中,依然問題重重

很多後端同學說我只負責寫接口,其他我一概不管,這樣造成的後果就是

1、接口結構無序、雜亂無章

2、接口和實際業務場景不相匹配、不可用

3、頻繁的同前端溝通,簡單的事情複雜化,前後端都很惱火

4、事情沒做好

後端在編寫接口前,首先是對業務的理解,在對業務未理解透徹之前,編碼都是無意義的,作爲後端來說,需要鍛鍊自己對整個系統全局考慮的能力,接口之間並非是毫無關聯的,我們在寫第一個接口之間,其他接口之間的業務邏輯也許考慮到,這在後端團隊合作開發不同功能的情況下顯得尤爲重要.

後端在開發接口時,我覺得主要從以下幾個方面需要注意:

接口url 定義

接口類型、參數

全局錯誤碼定義

接口json格式

接口文檔編寫

接口url定義

對於後端開發人員來說,接口前端入參,最終組合查詢數據庫資源,經過一系列相關業務場景下的計算,響應給前端json數據,每一層url的path定義需要清晰明瞭,這和後端在使用AOP定義事務管理同理,後端service需要滿足一定的命名規範,這樣方便統一管理,而且有這層規範後,後續的前後端對接會輕鬆很多

爲了在許多API和長時間內提供一致的開發人員體驗,API使用的所有名稱應爲:

  • 簡單

  • 直覺

  • 一致

這包括接口,資源,集合,方法和消息的名稱。

由於許多開發人員不是英文母語人士,因此這些命名約定的目標之一是確保大多數開發人員能夠輕鬆瞭解API。 它通過鼓勵在命名方法和資源時使用簡單,一致和小的詞彙表來實現。

  • API中使用的名稱應該是正確的美國英語。例如,許可證(而不是許可證),顏色(而不是顏色)。

  • 可以簡單地使用常用的簡短形式或長字的縮寫。例如,API優於應用程序編程接口。

  • 儘可能使用直觀,熟悉的術語。例如,當描述刪除(和銷燬)資源時,刪除是優先於擦除。

  • 對同一概念使用相同的名稱或術語,包括跨API共享的概念。

  • 避免名稱重載。爲不同的概念使用不同的名稱。

  • 仔細考慮使用可能與常用編程語言中的關鍵字衝突的名稱。可以使用這些名稱,但在API審查期間可能會觸發額外的審查。謹慎和謹慎地使用它們。

接口類型、參數

關於接口的請求類型,目前比較常用的:GETPOSTPUTDELETEPATCH

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給前端需要注意以下幾點:

1、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與之對應

2、所有返回接口數據需直接可用,越簡單越好

後端提供給前端的接口數據,最終交給前端的工作,只需要讓前端渲染數據即可,越簡單越好,不因摻雜過多的業務邏輯讓前端處理,所有複雜的業務邏輯,能合併規避掉的都需後端處理掉.

接口文檔編寫

接口文檔編寫是前後端對接重要依據,後端寫明接口文檔,前端根據接口文檔對接

文檔形勢目前主要分幾種:

1、依賴swagger框架,自動生成接口文檔(swagger只能生成基於key-value詳細參數方式,針對json格式,無法說明具體請求內容)

2、手動編寫說明文檔,推薦markdown編寫

接口對接

萬事俱備,只欠東風,雖然上面我們準備了所有我們該準備的,接口定義完美無缺,接口文檔也已說明,但在對接時任然可能出現問題,此時我想我們還需注意的細節

1、後端接口需自行進行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));
}

  

2、使用工具測試,推薦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命令行

3、前後端需心平氣和溝通,勿推卸責任,前後端開發人員水平不盡相同,作爲同事,需要的是團結合作,努力將事情做好,而非相互推卸

結語

前後端分離,簡化了我們的開發方式,不同人專注於不同的領域,技術價值最大化,大大提高工作效率,我們在掌握這些技能的同時,也需要加強自身的發展,以適應當前的技術發展趨勢,不管是前端還是後端,多瞭解一些,總是沒錯的,古人云:技多不壓身,我想也正是此理!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章