1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        字節(jié)都在用的代碼自動生成

        共 3114字,需瀏覽 7分鐘

         ·

        2023-06-07 12:10

        背景

        如果有一份接口定義,前端和后端都能基于此生成相應(yīng)端的代碼,不僅能降低前后端溝通成本,而且還能提升研發(fā)效率。

        字節(jié)內(nèi)部的 RPC 定義主要基于 thrift 實(shí)現(xiàn),thrift 定義了數(shù)據(jù)結(jié)構(gòu)和函數(shù),那么是否可以用來作為接口定義提供給前端使用呢?如果可以作為接口定義,是不是也可以通過接口定義自動生成請求接口的代碼呢?答案是肯定的,字節(jié)內(nèi)部已經(jīng)衍生出了多個(gè)基于 thrift 的代碼生成工具,本篇文章主要介紹如何通過 thrift 生成前端接口調(diào)用的代碼。

        接口定義

        接口定義,顧名思義就是用來定義接口的語言,由于字節(jié)內(nèi)部廣泛使用的 thrift 基本上滿足接口定義的要求,所以我們不妨直接把 thrift 當(dāng)成接口定義。

        thrift 是一種跨語言的遠(yuǎn)程過程調(diào)用 (RPC) 框架,如果你對 Typescript 比較熟悉的話,那它的結(jié)構(gòu)看起來應(yīng)該很簡單,看個(gè)例子:

              
              namespace?go?namesapce
        //?請求的結(jié)構(gòu)體
        struct?GetRandomRequest?{
        ?1:?optional?i32?min,
        ?2:?optional?i32?max,
        ?3:?optional?string?extra
        }
        //?響應(yīng)的結(jié)構(gòu)體
        struct?GetRandomResponse?{
        ?1:?optional?i64?random_num
        }
        //?定義服務(wù)
        service?RandomService?{
        ?GetRandomResponse?GetRandom?(1:?GetRandomRequest?req)
        }

        示例中的 service 可以看成是一組函數(shù),每個(gè)函數(shù)可以看成是一個(gè)接口。我們都知道,對于 restful 接口,還需要定義接口路徑(比如 /getUserInfo)和參數(shù)(query 參數(shù)、body 參數(shù)等),我們可以通過 thrift 注解來表示這些附加信息。

              
              namespace go namesapce
        struct GetRandomRequest {
        1: optional i32 min (api.source = "query"),
        2: optional i32 max (api.source = "query"),
        3: optional string extra (api.source = "body"),
        }
        struct GetRandomResponse {
        1: optional i64 random_num,
        }
        // Service
        service RandomService {
        GetRandomResponse GetRandom (1: GetRandomRequest req) (api.get = "/api/get-random"),
        }

        api.source 用來指定參數(shù)的位置,query 表示是 query 參數(shù),body 表示 body 參數(shù);api.get="/api/get-random" 表示接口路徑是 /api/get-random,請求方法是 GET;

        生成 Typescript

        上面我們已經(jīng)有了接口定義,那么對應(yīng)的 Typescript 應(yīng)該就呼之欲出了,一起來看代碼:

              
              interface?GetRandomRequest?{
        ??min:?number;
        ??max:?number;
        ??extra:?string;
        }
        interface?GetRandomResponse?{
        ??random_num:?number;
        }
        async?function?GetRandom(req:?GetRandomRequest):?Promise<GetRandomResponse>?{
        ??return?request({
        ????url:?'/api/get-random',
        ????method:?'GET',
        ????query:?{
        ??????min:?req.min,
        ??????max:?req.max,
        ????},
        ????body:?{
        ??????extra:?req.extra,
        ????},
        ??});
        }

        生成 Typescript 后,我們無需關(guān)心生成的代碼長什么樣,直接調(diào)用 GetRandom 即可。

        架構(gòu)設(shè)計(jì)

        要實(shí)現(xiàn)基于 thrift 生成代碼,最核心的架構(gòu)如下:

        c6f88dc1fdb9c70c7f2de3655ffaafaf.webp因?yàn)?thrift 的內(nèi)容我們不能直接拿來用,需要轉(zhuǎn)化成中間代碼(IR),這里的中間代碼通常是 json、AST 或者自定義的 DSL。如果中間代碼是 json,可能的結(jié)構(gòu)如下:

              
              {
        ??name:?'GetRandom',
        ??method:?'get',
        ??path:?'/api/get-random',
        ??req_schema:?{
        ????query_params:?[
        ??????{
        ????????name:?'min',
        ????????type:?'int',
        ????????optional:?true,
        ??????},
        ??????{
        ????????name:?'max',
        ????????type:?'int',
        ????????optional:?true,
        ??????},
        ????],
        ????body_params:?[
        ??????{
        ????????name:?'extra',
        ????????type:?'string',
        ????????optional:?true,
        ??????},
        ????],
        ????header_params:?[],
        ??},
        ??resp_schema:?{
        ????header_params:?[],
        ????body_params:?[],
        ??},
        };

        為了保持架構(gòu)的開放性,我們在核心鏈路上插入了 PrePlugin 和 PostPlugin,其中 PrePlugin 決定了 thrift 如何轉(zhuǎn)化成 IR,PostPlugin 決定 IR 如何生成目標(biāo)代碼。

        這里之所以是「目標(biāo)代碼」而不是「Typescript 代碼」,是因?yàn)槲蚁M煌?PostPlugin 可以產(chǎn)生不同的目標(biāo)代碼,比如可以通過 TSPostPlugin 生成 Typescript 代碼,通過 GoPostPlugin 生成 go 語言的代碼。

        總結(jié)

        代碼生成這塊的內(nèi)容還有很多可以探索的地方,比如如何解析 thrift?是找第三方功能生成 AST 還是通過 pegjs 解析成自定義的 DSL?多文件聯(lián)編如何處理、字段名 case 如何轉(zhuǎn)換、運(yùn)行時(shí)類型校驗(yàn)、生成的代碼如何與 useRequest 或 ReactQuery 集成等。

        thrift 其實(shí)可以看成接口定義的具體實(shí)現(xiàn),如果 thrift 不滿足你的業(yè)務(wù)場景,也可以自己實(shí)現(xiàn)一套類似的接口定義語言;接口定義作為前后端的約定,可以降低前后端的溝通成本;代碼生成,可以提升前端代碼的質(zhì)量和研發(fā)效率。

        如果本文對你有啟發(fā),歡迎點(diǎn)贊、關(guān)注、留言交流。

        作者:探險(xiǎn)家火焱https://juejin.cn/post/7220054775298359351
        瀏覽 73
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲无码一卡二卡 | 啊轻点灬太粗太长了黑人小说 | 色99999 | 国产主播第一页 | 美女扒开屁股秘 无遮挡 | 国产午夜精品久久久久久久 | 撞击稚嫩的小屁屁好紧 | 国产免费无遮挡免费视频在线看 | 亚洲你我色 | 美国黄色片一级片 |