Yapi 導(dǎo)出精美html的接口文檔

之前公司使用Yapi(我的建議少办,哈哈)來管理API券躁,但是公司要求提供API文檔方便與其他項目組對接艾扮。好在Yapi提供到處html接口文檔。但是問題來了筐高,請看下面的html導(dǎo)出效果搜囱。


演示鏈接

總體還是可以的,雖然不是特別好看柑土,但是還是看的過去的蜀肘。不過我們項目要求要有搜索功能。為此要編寫一個導(dǎo)出工具稽屏,在導(dǎo)出時動態(tài)的改變html的到想要的效果了扮宠。本來這里應(yīng)該開發(fā)一個Yapi插件的,但是基于其他考慮诫欠,最后決定使用Java開發(fā)涵卵。

總體采用
jcommander解析命令行輸入?yún)?shù)浴栽。
okhttp3進(jìn)行網(wǎng)絡(luò)請求。
jsouphtml進(jìn)行處理轿偎。
gsonjson進(jìn)行處理典鸡。
groovy自定義對導(dǎo)出的數(shù)據(jù)進(jìn)行處理。

項目結(jié)構(gòu)如下圖:
下面還是展示源代碼:

/**
 * @description: yapi導(dǎo)出
 * @author: zhanjinbing
 * @data: 2020-01-19 09:42
 */
public class Main {

    private static CommanderArgs commanderArgs = new CommanderArgs();
    private static String scriptText;
    private static GroovyObject groovyObject;

    public static void main(String[] args) {

        // 顯示版本信息
        Optional<String> versionOpt = Arrays.asList(args).stream()
                .filter(arg -> "-v".equals(arg) || "-version".equals(arg))
                .findFirst();

        if (versionOpt.isPresent()) {
            System.out.println("當(dāng)前版本為:" + commanderArgs.getVersion());
            return;
        }

        JCommander jCommander = JCommander.newBuilder().addObject(commanderArgs).build();
        try {
            jCommander.parse(args);
        } catch (ParameterException e) {
            System.out.println(e.getMessage());
            jCommander.usage();
            return;
        }

        System.out.println(
                        "╭╮╭╮╭━━╮╭━━╮╭━━╮ ┌──╮  ┐┐ ┐  ┌──┐ ╭─┌╯─╮\n" +
                        "┃╰╯┃┃╭╮┃┃╭╮┃╰╮╭╯┌┬──┬┐┌╯├─┼┘└╯╮ │ │││┌┐│\n" +
                        "╰╮╭╯┃╰╯┃┃╰╯┃ ┃┃  │  │ │││ │ ┌─┴─╯┐││││││\n" +
                        " ┃┃ ┃╭╮┃┃╭━╯ ┃┃  │  │ ││╯ │ ┌─┬──┐││││││\n" +
                        " ┃┃ ┃┃┃┃┃┃  ╭╯╰╮ └─┌╯  │──┼┘├─┼──┤││││││\n" +
                        " ╰╯ ╰╯╰╯╰╯  ╰━━╯└──╯─┘╰┘  ┘ ╰─┴──┘ ┘─╯┘╯\n");
        // 開始運行導(dǎo)出方案
        run();


    }

    private static void run() {

        // 執(zhí)行登錄過程
        YapiService yapiService = new YapiService(commanderArgs.getHost());

        try {
            yapiService.login(commanderArgs.getEmail(), commanderArgs.getPassword());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("登錄失敗");
            return;
        }

        YapiProjectInfo yapiProjectInfo;
        try {
            String projectInfo = yapiService.getProjectInfo(commanderArgs.getPid());
            JsonElement jsonElement = JsonParser.parseString(projectInfo);
            JsonObject data = jsonElement.getAsJsonObject().get("data").getAsJsonObject();
            Gson gson = new Gson();
            yapiProjectInfo = gson.fromJson(data, YapiProjectInfo.class);

            String msg = String.format("正在導(dǎo)出...《%s》中的%s", yapiProjectInfo.getName(), commanderArgs.getTypes());
            System.out.println(msg);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("獲取項目信息失敗");
            return;
        }

        if (commanderArgs.getScriptFile() != null) {
            try {
                scriptText = IOUtils.toString(new FileInputStream(commanderArgs.getScriptFile())
                        , Charset.defaultCharset());
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("腳本文件文本讀取異常");
                return;
            }

            GroovyClassLoader classLoader = new GroovyClassLoader();
            Class groovyClass = classLoader.parseClass(scriptText);
            try {
                groovyObject = (GroovyObject) groovyClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
                System.out.println("生成java類發(fā)生異常");
            }
        }

        // 導(dǎo)出指定類型的文件
        for (String type : commanderArgs.getTypes()) {
            String outputFilePath = String.format("%s%s%s.%s", System.getProperty("user.dir"),
                    File.separator, commanderArgs.getOutputFileName(), type);
            File outFile = new File(outputFilePath);
            if (outFile.exists() && !commanderArgs.isOverride()) {
                System.out.println(String.format("[%s]文件已存在坏晦,跳過", outputFilePath));
                continue;
            }

            try {
                String exportStr;
                if ("swagger".equalsIgnoreCase(type)) {
                    exportStr = yapiService.exportSwagger(commanderArgs.getPid(), commanderArgs.isWiki());
                } else {
                    exportStr = yapiService.export(type, commanderArgs.getPid(), commanderArgs.isWiki());
                }

                // 判斷是否有腳本要處理文件
                String outPutStr = (scriptText != null ? dealExportScript(type, exportStr) : exportStr);

                // 檢查處理后的文本
                if (outPutStr == null) {
                    System.out.println(String.format("[%s]處理后的文本為空萝玷,跳過", outputFilePath));
                    continue;
                }

                try (OutputStream outputStream = new FileOutputStream(outFile)) {
                    outputStream.write(outPutStr.getBytes(Charset.defaultCharset()));
                    outputStream.flush();
                    System.out.println(String.format("[%s]文件下載成功", outputFilePath));
                } catch (IOException e) {
                    System.out.println(String.format("[%s]文件寫入失敗", outputFilePath));
                    throw e;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String dealExportScript(String type, String exportStr) {
        String methodName = String.format("deal%s", type.toUpperCase());

        String dealStr;
        switch (type) {
            case "swagger":
            case "json":
                JsonElement jsonElement = JsonParser.parseString(exportStr);
                dealStr = invoke(methodName, exportStr, jsonElement);
                break;
            case "html":
                Document doc = Jsoup.parse(exportStr);
                dealStr = invoke(methodName, exportStr, doc);
                break;
            default:
                dealStr = invoke(methodName, exportStr);
                break;
        }
        return dealStr;
    }

    private static String invoke(String function, Object... objects) {
        Optional<MetaMethod> method =
                groovyObject.getMetaClass().getMethods().stream()
                        .filter(metaMethod -> metaMethod.getName().equals(function))
                        .findFirst();

        return (String) (method.isPresent() ?
                groovyObject.invokeMethod(function, objects) :
                objects[0]);
    }
}

這里我就只貼Main類中的代碼吧,減少篇幅昆婿。下面是github地址
https://github.com/zjbshk/yapi

下面是演示效果:


java -jar yapi-1.0-SNAPSHOT.jar -h [Yapi地址] -e [用戶名] -p [密碼]  -pid [項目id] -o [文件名] -t json -t html -s [script.groovy,腳本路徑]

精美的html導(dǎo)出工具就做好啦球碉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仓蛆,隨后出現(xiàn)的幾起案子睁冬,更是在濱河造成了極大的恐慌,老刑警劉巖看疙,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豆拨,死亡現(xiàn)場離奇詭異,居然都是意外死亡能庆,警方通過查閱死者的電腦和手機(jī)施禾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搁胆,“玉大人弥搞,你說我怎么就攤上這事∏裕” “怎么了攀例?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長一死。 經(jīng)常有香客問我肛度,道長傻唾,這世上最難降的妖魔是什么投慈? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮冠骄,結(jié)果婚禮上伪煤,老公的妹妹穿的比我還像新娘。我一直安慰自己凛辣,他們只是感情好抱既,可當(dāng)我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扁誓,像睡著了一般防泵。 火紅的嫁衣襯著肌膚如雪蚀之。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天捷泞,我揣著相機(jī)與錄音足删,去河邊找鬼。 笑死锁右,一個胖子當(dāng)著我的面吹牛失受,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咏瑟,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼拂到,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了码泞?” 一聲冷哼從身側(cè)響起兄旬,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎余寥,沒想到半個月后辖试,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡劈狐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年罐孝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肥缔。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡莲兢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出续膳,到底是詐尸還是另有隱情改艇,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布坟岔,位于F島的核電站谒兄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏社付。R本人自食惡果不足惜承疲,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸥咖。 院中可真熱鬧燕鸽,春花似錦、人聲如沸啼辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至党远,卻和暖如春削解,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沟娱。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工钠绍, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人花沉。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓柳爽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親碱屁。 傳聞我的和親對象是個殘疾皇子磷脯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,494評論 2 348