【開源項(xiàng)目】springfox-bridge:隨心所欲地為非restful接口生成API文檔

項(xiàng)目github地址: https://github.com/double-bin/springfox-bridge

一疆股、引言

????目前,利用swagger框架為restful接口編寫API文檔非常流行宿礁,在spring web項(xiàng)目中,利用springfox+swagger更是可以通過注解的方式直接進(jìn)行API文檔的生成恭垦,這樣開發(fā)者在項(xiàng)目開發(fā)的同時(shí)就直接把文檔準(zhǔn)備好了邻吭,利用springfox的配置,可以在項(xiàng)目啟動(dòng)后直接瀏覽器訪問查看API文檔焕檬,同時(shí)還能在界面直接進(jìn)行API的測(cè)試姆坚。springfox的使用本文不在此贅述了,現(xiàn)在引出一個(gè)問題: 非restful接口能否采用swagger生成接口文檔实愚?

????在項(xiàng)目中集成springfox-bridge可以快速的為非restful接口生成API文檔兼呵,編寫文檔的方式跟springfox一樣簡(jiǎn)單,在相關(guān)類或者接口上采用注解的方式定義文檔信息即可腊敲。

????springfox-bridge相當(dāng)于架設(shè)了一座與springfox之間的橋梁击喂,通過動(dòng)態(tài)生成配置了springfox注解的mvc接口并進(jìn)行注冊(cè),形成對(duì)非restful接口生成swagger文檔的能力碰辅。

二懂昂、springfox-bridge特性說明

  1. 啟動(dòng)簡(jiǎn)單
  • 在springboot項(xiàng)目中,集成springfox-bridge-spring-boot-starter即可自動(dòng)啟動(dòng)没宾;
  • 在非springboot項(xiàng)目中凌彬,通過實(shí)現(xiàn)ApplicationContextAware接口,通過SpringfoxBridge.start(ApplicationContext context)方法,并配置@EnableSwagger2注解即可快速啟動(dòng)循衰。
  1. 兼容性強(qiáng)
  • 與協(xié)議無關(guān)铲敛,不挑協(xié)議,無論你是使用dubbo会钝、ServiceComb還是其它種種伐蒋,只要項(xiàng)目本身啟用了springmvc, 相應(yīng)的接口注冊(cè)了spring bean, 就能像使用springfox那樣使用springfox-brige,用注解的方式為接口生成文檔迁酸。
  • 更進(jìn)一步的講先鱼,只要滿足上述條件的spring bean, 即使不是controller層的接口,也能使用springfox-bridge進(jìn)行文檔生成奸鬓。
  1. 簡(jiǎn)單的注解
  • springfox-bridge提供了幾個(gè)簡(jiǎn)單的注解供開發(fā)使用型型,注解的使用方式與springfox的類似,主要在類/接口全蝶、方法上進(jìn)行文檔的定義。
  1. 方便的分組
  • 采用@BridgeGroup注解可以方便的為項(xiàng)目的接口文檔進(jìn)行分組寺枉,而無需手動(dòng)的配置Docket抑淫,springfox-bridge自動(dòng)按照@BridgeGroup的注解值將文檔進(jìn)行分組歸類。
  1. 不影響原有文檔
  • springfox-bridge通過分組隔離姥闪,項(xiàng)目中原先使用springfox為restful接口生成的文檔始苇,不會(huì)受到springfox-bridge的影響
  1. 方法入?yún)⒉幌薅ㄕ?qǐng)求體的數(shù)量
  • 原生springfox對(duì)restful請(qǐng)求生成文檔,而restful只支持一個(gè)請(qǐng)求體入?yún)ⅲㄓ聾RequestBody注解標(biāo)識(shí))筐喳。springfox-bridge沒有這個(gè)限制催式。
  1. 支持界面測(cè)試
  • 跟springfox生成文檔可以通過界面直接調(diào)用一樣函喉,springfox-bridge同樣支持

三、使用說明

使用springfox-bridge需要項(xiàng)目本身啟用了springmvc框架荣月, spring相關(guān)依賴版本在spring3.1以上

3.1 配置maven依賴

1)使用了springboot的項(xiàng)目:

        <dependency>
            <groupId>com.github.double-bin</groupId>
            <artifactId>springfox-bridge-spring-boot-starter</artifactId>
            <version>1.0.8</version>
        </dependency>
  1. 非springboot項(xiàng)目:
        <dependency>
            <groupId>com.github.double-bin</groupId>
            <artifactId>springfox-bridge-core</artifactId>
            <version>1.0.8</version>
        </dependency>

3.2 啟動(dòng)配置

1)使用了springboot的項(xiàng)目

  • 配置了springfox-bridge-spring-boot-starter后管呵,默認(rèn)開啟springfox-bridge。
  • 如果需要關(guān)閉哺窄,可以在application.properties文件(或yml文件)中配置springfox.bridge.enabled的值為false即可
  1. 非springboot項(xiàng)目:
    可通過配置類實(shí)現(xiàn)ApplicationContextAware接口的setApplicationContext方法捐下,方法實(shí)現(xiàn)中通過SpringfoxBridge.start()方法啟動(dòng)springfox-bridge, 配置類上通過@EnableSwagger2啟動(dòng)springfox基本功能, 可參考:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import com.github.doublebin.springfox.bridge.core.SpringfoxBridge;
import springfox.documentation.swagger2.annotations.EnableSwagger2;


@EnableSwagger2
@Configuration
public class MyXXXConfiguration implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringfoxBridge.start(applicationContext);

    }
}

3.3 使用示例

下面示例代碼演示如何使用springfox-bridge的注解萌业,如何定義的文檔坷襟,如果設(shè)置分組等,展示結(jié)果請(qǐng)看3.4

1, 定義兩個(gè)請(qǐng)求的model類:TestRquest1和TestRequest2

model類中可以使用springfox的原生注解:io.swagger.annotations.ApiModel和io.swagger.annotations.ApiModelProperty

package com.github.doublebin.springfox.bridge.demo.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@ApiModel(value="TestRequest1", description = "測(cè)試請(qǐng)求體1")
public class TestRequest1 {

    @ApiModelProperty(value = "唯一id", required = true)
    private long uuid;

    @ApiModelProperty(value = "名字", required = true)
    private String name;
}

package com.github.doublebin.springfox.bridge.demo.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@ApiModel(value="TestRequest2", description = "測(cè)試請(qǐng)求體2")
public class TestRequest2 {

    @ApiModelProperty(value = "名字", required = true)
    private String name;

    @ApiModelProperty(value = "描述", required = true)
    private String desc;
}

2, 定義三個(gè)service類生年,并標(biāo)注@Service供spring掃描并注冊(cè)bean婴程。
package com.github.doublebin.springfox.bridge.demo.service;

import com.github.doublebin.springfox.bridge.demo.model.TestRequest1;
import com.github.doublebin.springfox.bridge.demo.model.TestRequest2;
import org.springframework.stereotype.Service;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeApi;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeGroup;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeModelProperty;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeOperation;

@Service
@BridgeApi(value = "TestService1 Apis", description = "測(cè)試服務(wù)1")
@BridgeGroup("test-group1")
public class TestService1 {

    @BridgeOperation(value = "測(cè)試查詢1", notes = "測(cè)試查詢方法1說明")
    public String testQuery(@BridgeModelProperty(value = "用戶id", required = true) long id, @BridgeModelProperty(value = "請(qǐng)求2", required = false) TestRequest1 request){
        return "Test query success, id is " + id;
    }

    @BridgeOperation(value = "測(cè)試查詢2", notes = "測(cè)試查詢方法2說明")
    public String testQuery(@BridgeModelProperty(value = "用戶id", required = true) long id){
        return "Test query success, id is " + id;
    }

    @BridgeOperation(value = "測(cè)試查詢3", notes = "測(cè)試查詢方法3說明")
    public String testQuery(){
        return "Test query success.";
    }

    @BridgeOperation(value = "測(cè)試查詢4", notes = "測(cè)試查詢方法4說明")
    public String testQuery(@BridgeModelProperty(value = "用戶id", required = true) long id, @BridgeModelProperty(value = "請(qǐng)求2", required = false) TestRequest2 request){
        return "Test query success, id is " + id;
    }

}
package com.github.doublebin.springfox.bridge.demo.service;

import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeApi;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeGroup;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeModelProperty;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeOperation;
import com.github.doublebin.springfox.bridge.demo.model.TestRequest1;
import org.springframework.stereotype.Service;

@Service
@BridgeApi(value = "TestService2 Apis", description = "測(cè)試服務(wù)2")
@BridgeGroup("test-group2")
public class TestService2 {
    @BridgeOperation(value = "測(cè)試查詢", notes = "測(cè)試查詢方法說明")
    public String testQuery(@BridgeModelProperty(value = "用戶id", required = true) long id, @BridgeModelProperty(value = "請(qǐng)求2", required = false) TestRequest1 request){
        return "Test query success, id is " + id;
    }
}

package com.github.doublebin.springfox.bridge.demo.service;

import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeApi;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeGroup;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeModelProperty;
import com.github.doublebin.springfox.bridge.core.builder.annotations.BridgeOperation;
import com.github.doublebin.springfox.bridge.demo.model.TestRequest1;
import org.springframework.stereotype.Service;

@Service
@BridgeApi(value = "TestService3 Apis", description = "測(cè)試服務(wù)3")
@BridgeGroup("test-group1")
public class TestService3 {
    @BridgeOperation(value = "測(cè)試查詢", notes = "測(cè)試查詢方法說明")
    public String testQuery(@BridgeModelProperty(value = "用戶id", required = true) long id, @BridgeModelProperty(value = "請(qǐng)求2", required = false) TestRequest1 request){
        return "Test query success, id is " + id;
    }
}

?

????示例中定義了2個(gè)分組:test-group1 和 test-group2, 其中TestService1和TestService3歸屬于test-group1分組抱婉,TestService2歸屬于test-group2分組档叔,其中TestService1中定義了多個(gè)不同的方法,在3.4節(jié)中會(huì)展示這些情況下的多個(gè)效果

3.4 示例效果展示

瀏覽器訪問地址:http:${host}:${port}/${server.context-path}/swagger-ui.html

  1. 首先看下面兩個(gè)分組的截圖授段,其中test-group1:
test-group1.png

test-group2:

test-group2.png

說明:

  • 用spring-fox對(duì)某個(gè)類生成API文檔蹲蒲,必須用在類上使用@BridgeApi注解,并在需要生成文檔的方法上使用@BridgeOperation注解侵贵。

  • 如果要對(duì)某個(gè)類分組届搁,可以在類上標(biāo)識(shí)@BridgeGroup注解。前面示例中窍育,通過@BridgeGroup注解定義了2個(gè)分組卡睦,將3個(gè)Service類進(jìn)行了歸類,在上面兩圖可以看到漱抓,通過下拉框切換分組后表锻,將分別展示@BridgeGroup注解定義的不同分組的頁面。當(dāng)然@BridgeGroup如果不定義乞娄,springfox-bridge會(huì)生成一個(gè)名為defafult的分組瞬逊,將沒有顯式定義@BridgeGroup注解的文檔歸類到default分組下。

  • swagger頁面上類的tag采用類全名的方式展示仪或,@BridgeApi注解的description值也會(huì)展示在界面上描述類的作用确镊。

  • @BridgeOperation注解定義方法,springfox-bridge會(huì)在對(duì)應(yīng)的類tag下生成對(duì)應(yīng)的方法tag范删,由圖可以看出蕾域,path路徑的組成格式為:“/類全名/方法名/入?yún)⒌念惷鬃帜附M合”。

  • 如果@BridgeOperation定義的方法沒有入?yún)ⅲ敲磒ath路徑中則沒有“入?yún)⒌念愂鬃帜附M合”旨巷;如果兩個(gè)同名方法入?yún)⒌念惷鬃帜附M合相同巨缘,那么第二個(gè)及之后的同名方法的path路徑會(huì)追加“/index數(shù)字”以區(qū)分不同的方法,index的排序以springfox-bridge內(nèi)部對(duì)方法加載的順序進(jìn)行排序采呐。

  • @BridgeOperation注解的value值標(biāo)識(shí)該方法的簡(jiǎn)要說明若锁,跟path在同一行展示。

  • @BridgeModelProperty注解可以對(duì)方法入?yún)⑦M(jìn)行標(biāo)識(shí)懈万,用以對(duì)入?yún)⒓右哉f明

  • 方法入?yún)㈩愋腿绻且粋€(gè)model類拴清,該model類可以用io.swagger包的原生注解@ApiModel(標(biāo)識(shí)類)和@ApiModelProperty(標(biāo)識(shí)字段)對(duì)model類進(jìn)行說明,之所以用原生注解会通,是為了兼容原生springfox口予,不必重復(fù)定義注解。

  • 在swagger界面上可以看到涕侈,springfox-bridge對(duì)每個(gè)標(biāo)識(shí)了@BridgeOperation的方法都動(dòng)態(tài)生成一個(gè)post請(qǐng)求沪停,并動(dòng)態(tài)生成一個(gè)body請(qǐng)求體,方法的所有入?yún)⒍甲鳛樾抡?qǐng)求體的字段裳涛。

四木张、springfox-bridge注解說明

4.1 springfox-bridge自定義注解
注解 位置 主要字段 說明 對(duì)標(biāo)原生注解
BridgeApi description 對(duì)類進(jìn)行說明 io.swagger.annotations.Api
BridgeGroup value 標(biāo)識(shí)分組
BridgeOperation 方法 value、notes value端三、notes分別標(biāo)識(shí)方法概要和詳細(xì)說明 io.swagger.annotations.ApiOperation
BridgeModelProperty 入?yún)?/td> value 標(biāo)識(shí)入?yún)⒄f明 io.swagger.annotations.ApiModelProperty
4.2 兼容的springfox swagger原生注解

目前兼容入?yún)⒌恼?qǐng)求體的model用io.swagger包的原生注解@ApiModel@ApiModelProperty 舷礼,后續(xù)會(huì)提供其它支持

五、展望

目前springfox-bridge的1.0.8版本已經(jīng)發(fā)布到maven中央倉(cāng)庫(kù)郊闯,后續(xù)會(huì)持續(xù)更新妻献,支持更多的特性, 比如: 兼容更多的springfox swagger的原生注解、支持返回體說明团赁、支持author自定義育拨、支持入?yún)㈩愋腿故镜取m?xiàng)目源碼見github: https://github.com/double-bin/springfox-bridge

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欢摄,一起剝皮案震驚了整個(gè)濱河市熬丧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怀挠,老刑警劉巖析蝴,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绿淋,居然都是意外死亡嫌变,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門躬它,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人东涡,你說我怎么就攤上這事冯吓√却” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵组贺,是天一觀的道長(zhǎng)凸舵。 經(jīng)常有香客問我,道長(zhǎng)失尖,這世上最難降的妖魔是什么啊奄? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮掀潮,結(jié)果婚禮上菇夸,老公的妹妹穿的比我還像新娘。我一直安慰自己仪吧,他們只是感情好庄新,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薯鼠,像睡著了一般择诈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上出皇,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天羞芍,我揣著相機(jī)與錄音,去河邊找鬼郊艘。 笑死荷科,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的暇仲。 我是一名探鬼主播步做,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奈附!你這毒婦竟也來了全度?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤斥滤,失蹤者是張志新(化名)和其女友劉穎将鸵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佑颇,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顶掉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挑胸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痒筒。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出簿透,到底是詐尸還是另有隱情移袍,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布老充,位于F島的核電站葡盗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啡浊。R本人自食惡果不足惜觅够,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巷嚣。 院中可真熱鬧喘先,春花似錦、人聲如沸涂籽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽评雌。三九已至树枫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間景东,已是汗流浹背砂轻。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斤吐,地道東北人搔涝。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像和措,于是被迫代替她去往敵國(guó)和親庄呈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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