API文檔生成之Springfox-Swagger框架的簡(jiǎn)單使用

后端開發(fā)一定厭倦了和前端對(duì)API接口的麻煩,之前還是小白時(shí)愣是手寫word文檔給前端工程師使用借卧,各種copy字段加解釋...

然而盹憎,有更好用的API框架可以使用,可以讓我們擺脫這種煩惱铐刘,下面說下spring-fox swagger的簡(jiǎn)單使用
注:swagger是一種API規(guī)范陪每,springfox是其規(guī)范的一種實(shí)現(xiàn)

1. 引入swagger依賴

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>

2. 配置swagger,本例中使用的是springboot配置镰吵,如需xml配置請(qǐng)自行百度

編寫SwaggerConfig
添加注解
@Configuration
@EnableSwagger2
Docket.apis()中配置的包名為要掃描生成API的Controller包名檩禾,將會(huì)為該包下的controller生成相關(guān)文檔
ApiInfo為相關(guān)信息,該信息將會(huì)展示在文檔中

/**
 * Created by wangqichang on 2018/6/22.
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.kichun.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("炒雞好用的系統(tǒng)API")
                .description("更多內(nèi)容請(qǐng)咨詢開發(fā)者 王啟昌")
                .termsOfServiceUrl("http://kichun.xin/")
                .contact("wangqichang")
                .version("1.0")
                .build();
    }
}

3. 在需要添加的controller類和方法中加上api注解

加在類上
@Api(value = "字典操作接口",tags = {"字典常量操作"})
加在方法上
@ApiOperation(value = "刪除字典類型",notes = "")
@ApiImplicitParam(name = "id",value = "字典ID",required = true,dataType = "String")

/**
 *
 * @author wangqichang
 * @since 2018-06-15
 */
@Api(value = "字典操作接口",tags = {"字典常量操作"})
@RestController
@RequestMapping("/dataDict")
public class DataDictController {
    @Autowired
    private DataDictService dataDictService;

    @ApiOperation(value = "刪除字典類型",notes = "")
    @ApiImplicitParam(name = "id",value = "字典ID",required = true,dataType = "String")
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public ResultDTO delete(String id) {

        return null疤祭;
    }

當(dāng)有多個(gè)參數(shù)時(shí)用@ApiImplicitParams

    /**
     * 登錄
     *
     * @author Qichang.Wang
     * @date 17:27 2018/6/14
     */
    @ApiOperation(value = "用戶登錄接口", notes = "")
    @ApiImplicitParams(value = { @ApiImplicitParam(name = "name", value = "用戶名", required = true),
            @ApiImplicitParam(name = "pwd", value = "密碼", required = true),
            @ApiImplicitParam(name = "rememberMe", value = "勾選時(shí)傳遞true", required = false) })
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResultDTO login(String name, String pwd, String rememberMe) {
    
        return new ResultDTO(200, "登錄成功盼产!");
    }

當(dāng)參數(shù)是個(gè)對(duì)象時(shí),參數(shù)前加入
@RequestBody
這樣才能將對(duì)象的屬性給到swagger

 * 新增更新字典類型
     *
     * @author Qichang.Wang
     * @date 16:05 2018/6/15
     */
    @ApiOperation(value = "新增及更新字典",notes = "對(duì)象包含ID時(shí)為更新勺馆,不包含ID時(shí)為新增")
    @ApiImplicitParam(name = "dict",value = "字典對(duì)象,詳細(xì)請(qǐng)查看對(duì)象屬性",required = true,dataType = "DataDict")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public ResultDTO update(@RequestBody @ModelAttribute DataDict dict) {
        return new ResultDTO(200,"操作成功");
    }

4. web界面展示

注解的各個(gè)屬性相信各位大佬一看就能明了戏售,不多加解釋了
加好注解后,重啟應(yīng)用
接下來是見證奇跡的時(shí)候
訪問路徑:應(yīng)用地址+端口+swagger-ui.html 例如http://localhost:8800/swagger-ui.html

image.png

展開contoller效果


image.png

展開接口效果


image.png

點(diǎn)try it out 可以直接對(duì)接口進(jìn)行測(cè)試草穆,徹底拋棄什么Postman灌灾,RestfulClient等調(diào)試工具!

只要注釋寫的全续挟,再復(fù)雜的應(yīng)用統(tǒng)統(tǒng)搞定有沒有=糇洹!诗祸!
改了接口自動(dòng)重新生成API文檔有沒有E芊肌V嶙堋!
會(huì)寫代碼的前端工程師都能看懂有沒有2└觥;痴痢!
還是看不懂的前端直接打屎算了有沒有E栌丁Mぁ!

美中不足是字段詳細(xì)我還不確定能不能加注釋共耍,你們研究下吧虑灰,我覺得達(dá)到這效果已經(jīng)OK了

20180921補(bǔ)充:
針對(duì)接口請(qǐng)求對(duì)象及返回對(duì)象使用上述方式無法展示字段說明。使用@ModelAttribute注解可能會(huì)導(dǎo)致一些莫名錯(cuò)誤無法獲取參數(shù)

5. 實(shí)體注解

@ApiModel 用于實(shí)體類上痹兜,標(biāo)注為需要生成文檔的實(shí)體
@ApiModelProperty(value = "url菜單",required = true) 用于實(shí)體屬性穆咐,標(biāo)注該屬性的說明,require 為false時(shí)不會(huì)在頁面上進(jìn)行展示該屬性

@Data
@Accessors(chain = true)
@TableName("t_resource")
@ApiModel
public class Resource extends Model<Resource> {

    private static final long serialVersionUID = 1L;

    /**
     * 資源ID
     */
    @TableId(type = IdType.UUID)
    @ApiModelProperty(hidden = true)
    private String id;

    /**
     * url
     */
    @ApiModelProperty(value = "url菜單",required = true)
    private String url;

    /**
     * 父ID
     */
    @TableField("parent_id")
    @ApiModelProperty(hidden = true)
    private String parentId;

    /**
     * 資源名
     */
    @TableField("resource_name")
    @ApiModelProperty(value = "資源名",required = true)
    private String resourceName;

    @Override
    protected Serializable pkVal() {
        return this.id;
    }

}

在實(shí)體添加注解后字旭,在接口中使用如下注解

@ApiParam(name = "資源對(duì)象",value = "json格式",required = true)
標(biāo)識(shí)該對(duì)象為文檔實(shí)體

    @ApiOperation(value = "權(quán)限資源新增及更新", notes = "有ID更新对湃,沒ID為新增")
    @RequestMapping(value = "/resource", method = RequestMethod.POST)
    public ResultDTO resource(@RequestBody @ApiParam(name = "資源對(duì)象",value = "json格式",required = true) Resource resource) {
        Integer count = 0;
        try {
            count = userService.updateResources(resource);
        } catch (Exception e) {
            log.error("新增或更新資源異常:{}", e.getMessage());
            return new ResultDTO(500, e.getMessage());
        }
        return count > 0 ? new ResultDTO(200, "保存成功") : new ResultDTO(500, "操作失敗,請(qǐng)查詢?nèi)罩?);
    }

實(shí)體注釋文檔界面

需要點(diǎn)擊model才會(huì)顯示說明


image.png

效果如下


image.png

6. 關(guān)于shiro權(quán)限攔截

使用中發(fā)現(xiàn)開啟shiro后無法訪問swagger api界面
問題原因:swagger內(nèi)置接口被權(quán)限攔截導(dǎo)致無法訪問
處理方式:
瀏覽器打開F12調(diào)試遗淳,選擇network拍柒,查看status為非200的以swagger、springfox屈暗、apidoc開頭的URL拆讯,記錄該URL并在權(quán)限攔截中放行

image.png

例如shiro,在ShiroFilter中放行以下URL养叛。注意不同版本的swaggerURL略有區(qū)別往果,請(qǐng)以上面的方式查看具體需放行的URL

        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");

7. 前后分離之token Authorization 請(qǐng)求頭設(shè)

在前后分離項(xiàng)目中部分項(xiàng)目會(huì)使用到j(luò)wt或者其他方式在header中傳遞令牌token的方式,需要swagger添加相關(guān)配置加入請(qǐng)求頭

  1. 在swaggerConfig中一铅,加入全局header設(shè)置,參考如下代碼
/**
 * Created by wangqichang on 2018/9/25.
 */
@Slf4j
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Value("${SwaggerSwitch:false}")
    private boolean SwaggerSwitch;

    @Bean
    public Docket createRestApi() {
        log.info("========================================== 當(dāng)前環(huán)境是否開啟Swagger:" + SwaggerSwitch);
        return new Docket(DocumentationType.SWAGGER_2)
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts())
                .enable(SwaggerSwitch)
                .apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.basePackage("com.seebon.vip.csp.server.web.controller.rest")).paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("VIP后臺(tái)系統(tǒng)API").description("新后臺(tái)Restful接口").termsOfServiceUrl("http://www.xxx.com")
                .version("1.0").build();
    }

    /**
     * swagger加入全局Authorization header 將在ui界面右上角新增token輸入界面
     * @return
     */
    private List<ApiKey> securitySchemes() {
        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        ArrayList arrayList = new ArrayList();
        arrayList.add(apiKey);
        return arrayList;
    }

    /**
     * 在Swagger2的securityContexts中通過正則表達(dá)式堕油,設(shè)置需要使用參數(shù)的接口(或者說潘飘,是去除掉不需要使用參數(shù)的接口),
     * 如下列代碼所示掉缺,通過PathSelectors.regex("^(?!auth).*$")卜录,
     * 所有包含"auth"的接口不需要使用securitySchemes。即不需要使用上文中設(shè)置的名為“Authorization”眶明,
     * type為“header”的參數(shù)艰毒。
     *
     */
    private List<SecurityContext> securityContexts() {
        SecurityContext build = SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.any())
                .build();
        ArrayList arrayList = new ArrayList();
        arrayList.add(build);
        return arrayList;
    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        SecurityReference authorization = new SecurityReference("Authorization", authorizationScopes);
        ArrayList arrayList = new ArrayList<>();
        arrayList.add(authorization);
        return arrayList;
    }
}

加入以上代碼后,swagger-ui界面右上角將出現(xiàn)如圖圖標(biāo)


image.png

點(diǎn)擊該圖標(biāo)搜囱,在value中輸入登錄返回的token


image.png

然后請(qǐng)求任何接口丑瞧,都會(huì)帶上一個(gè)名為Authorization柑土,值為輸入值(例如本文中的token)的請(qǐng)求頭了
image.png

8. 多項(xiàng)目多模塊集成swagger

在spring-cloud微服務(wù)項(xiàng)目下,每個(gè)服務(wù)都集成swagger后绊汹,我們有了多個(gè)swagger-ui.html地址稽屏,這時(shí)候往往不同的服務(wù)接口需要訪問不同的URL才能看到對(duì)應(yīng)的swagger.html界面

如何將多個(gè)服務(wù)的swagger接口集中在一個(gè)頁面進(jìn)行展示?使用spring cloud zuul網(wǎng)關(guān)西乖, 并做相應(yīng)的配置即可

  1. zull網(wǎng)關(guān)啟動(dòng)類狐榔,開啟swagger
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableZuulProxy
@EnableSwagger2
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class);
    }
}
  1. 實(shí)現(xiàn)接口SwaggerResourcesProvider 并加入注解@Component @Primary確保該類能被掃描為該接口的主配置
/**
 * @author wangqichang
 * @since 2019/7/4
 */


@Primary
public class MySwaggerResourceProvider implements SwaggerResourcesProvider {



    @Override
    public List<SwaggerResource> get() {
        List resources = new ArrayList<>();
        resources.add(swaggerResource("系統(tǒng)及用戶API", "/usermanager/v2/api-docs", "1.0"));
        resources.add(swaggerResource("線路及臺(tái)賬API", "/linemanager/v2/api-docs", "1.0"));
        resources.add(swaggerResource("文件服務(wù)管理API", "/filemanager/v2/api-docs", "1.0"));
        return resources;
    }


    private SwaggerResource swaggerResource(String name, String location, String version) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(version);
        return swaggerResource;
    }
}
  1. 效果展示
    在ui界面Select a spec中,出現(xiàn)了我們配置的三個(gè)SwaggerResource获雕,當(dāng)我們進(jìn)行選擇時(shí)薄腻,將直接訪問改配置的URL。
    注意届案,使用zuul將地址路由到實(shí)際微服務(wù)的地址的/v2/api-docs接口
image.png

踩坑記錄

采坑1
項(xiàng)目使用了sitemesh時(shí)庵楷,需要在decorators.xml中把上述資源進(jìn)行排除,否則會(huì)影響到swagger-ui資源的訪問萝玷,導(dǎo)致swagger-ui界面無內(nèi)容

采坑2
在自定義權(quán)限控制中嫁乘,訪問/swagger-resources/configuration/ui資源時(shí)404,原因?yàn)橥ㄟ^@EnableSwagger2時(shí)會(huì)自動(dòng)掃描到一個(gè)ApiResourceController類球碉,該類有個(gè)資源路徑為/configuration/ui
而在自定義權(quán)限控制中該路徑被攔截返回錯(cuò)誤導(dǎo)致404

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜓斧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子睁冬,更是在濱河造成了極大的恐慌挎春,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豆拨,死亡現(xiàn)場(chǎng)離奇詭異直奋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)施禾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門脚线,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弥搞,你說我怎么就攤上這事邮绿。” “怎么了攀例?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵船逮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我粤铭,道長(zhǎng)挖胃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮酱鸭,結(jié)果婚禮上吗垮,老公的妹妹穿的比我還像新娘。我一直安慰自己凛辣,他們只是感情好抱既,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扁誓,像睡著了一般防泵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝗敢,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天捷泞,我揣著相機(jī)與錄音,去河邊找鬼寿谴。 笑死锁右,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讶泰。 我是一名探鬼主播咏瑟,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼痪署!你這毒婦竟也來了码泞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤狼犯,失蹤者是張志新(化名)和其女友劉穎余寥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悯森,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宋舷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓢姻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祝蝠。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖幻碱,靈堂內(nèi)的尸體忽然破棺而出续膳,到底是詐尸還是另有隱情,我是刑警寧澤收班,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站谒兄,受9級(jí)特大地震影響摔桦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一邻耕、第九天 我趴在偏房一處隱蔽的房頂上張望鸥咖。 院中可真熱鬧,春花似錦兄世、人聲如沸啼辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸥拧。三九已至,卻和暖如春削解,著一層夾襖步出監(jiān)牢的瞬間富弦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工氛驮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腕柜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓矫废,卻偏偏與公主長(zhǎng)得像盏缤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蓖扑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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