博客專欄

        EEPW首頁(yè) > 博客 > Springfox與SpringDoc——swagger如何選擇(SpringDoc入門(mén))

        Springfox與SpringDoc——swagger如何選擇(SpringDoc入門(mén))

        發(fā)布人:天翼云開(kāi)發(fā)者 時(shí)間:2023-04-07 來(lái)源:工程師 發(fā)布文章

        本文分享自天翼云開(kāi)發(fā)者社區(qū)@《Springfox與SpringDoc——swagger如何選擇(SpringDoc入門(mén))》,作者: 才開(kāi)始學(xué)技術(shù)的小白

        鏈接:

        https://www.ctyun.cn/developer/article/381687816880197?track=|cp:cz_bk|tgdy:wenzhang|ttjh:bokeshequ|key:bw308|pf:PC

        0.引言

        之前寫(xiě)過(guò)一篇關(guān)于swagger(實(shí)際上是springfox)的使用指南(https://www.ctyun.cn/developer/article/371704742199365),涵蓋了本人在開(kāi)發(fā)與學(xué)習(xí)的時(shí)候碰到的各種大坑。但由于springfox已經(jīng)不更新了,很多項(xiàng)目都在往springdoc遷移

        筆者也是花了一些時(shí)間試了一下這個(gè)號(hào)稱“把springfox按在地下摩擦”的springdoc究竟好不好使,本文就來(lái)簡(jiǎn)單介紹下springdoc的使用以及優(yōu)劣勢(shì)

        1.引入maven依賴

        這里有個(gè)大坑一定要注意!!!

        如果你跟我一樣,現(xiàn)在使用的是springfox,但是想往springdoc遷移,結(jié)果試了一下發(fā)現(xiàn)還是springfox好用/懶得改那么多注解,還是想換回springfox,一定要把springdoc的maven依賴刪掉!!!不然springboot會(huì)默認(rèn)你用的是springdoc,導(dǎo)致swagger界面出不來(lái)

        <dependency>

                    <groupId>org.springdoc</groupId>

                    <artifactId>springdoc-openapi-ui</artifactId>

                    <version>1.6.11</version>

         </dependency>

        2.springdoc配置類

        實(shí)際上springdoc的配置非常簡(jiǎn)單,使用的是OpenAPI類與GroupedOpenApi來(lái)配置

        /**

         * SpringDoc API文檔相關(guān)配置

         * Created by macro on 2023/02/02.

         */@Configurationpublic class SpringDocConfig {

            @Bean

            public OpenAPI mallTinyOpenAPI() {

                return new OpenAPI()

                        .info(new Info().title("CTYUN API")

                                .description("SpringDoc API 演示")

                                .version("v1.0.0")

                                .license(new License().name("Apache 2.0").url("https://github.com/")))

                        .externalDocs(new ExternalDocumentation()

                                .description("SpringBoot項(xiàng)目")

                                .url("http://www.ctyun.com"));

            }

         

            @Bean

            public GroupedOpenApi adminApi() {

                return GroupedOpenApi.builder()

                        .group("admin")

                        .pathsToMatch("/**")

                        .build();

            }

            //可以創(chuàng)建不同的GroupedOpenApi來(lái)判斷不同的controller

            @Bean

            public GroupedOpenApi userApi() {

                return GroupedOpenApi.builder()

                        .group("user")

                        .pathsToMatch("/user/**")

                        .build();

            }}

        3.啟動(dòng)

        默認(rèn)配置之后直接進(jìn)入:http://localhost:8080/swagger-ui/index.html 即可

        注意這里與springfox略有不同(http://localhost:8080/swagger-ui.html)

        image.png 

        4.與SpringFox的注解對(duì)照

        image.png 

        5.SpringDoc基本注解用法

        這里轉(zhuǎn)載一個(gè)寫(xiě)的非常全面的示例接口,原文可以去看:https://blog.csdn.net/zhenghongcs/article/details/123812583

        /**

         * 品牌管理Controller

         * Created by macro on 2019/4/19.

         */@Tag(name = "PmsBrandController", description = "商品品牌管理")@Controller@RequestMapping("/brand")public class PmsBrandController {

            @Autowired

            private PmsBrandService brandService;

         

            private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

         

            @Operation(summary = "獲取所有品牌列表",description = "需要登錄后訪問(wèn)")

            @RequestMapping(value = "listAll", method = RequestMethod.GET)

            @ResponseBody

            public CommonResult<List<PmsBrand>> getBrandList() {

                return CommonResult.success(brandService.listAllBrand());

            }

         

            @Operation(summary = "添加品牌")

            @RequestMapping(value = "/create", method = RequestMethod.POST)

            @ResponseBody

            @PreAuthorize("hasRole('ADMIN')")

            public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {

                CommonResult commonResult;

                int count = brandService.createBrand(pmsBrand);

                if (count == 1) {

                    commonResult = CommonResult.success(pmsBrand);

                    LOGGER.debug("createBrand success:{}", pmsBrand);

                } else {

                    commonResult = CommonResult.failed("操作失敗");

                    LOGGER.debug("createBrand failed:{}", pmsBrand);

                }

                return commonResult;

            }

         

            @Operation(summary = "更新指定id品牌信息")

            @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)

            @ResponseBody

            @PreAuthorize("hasRole('ADMIN')")

            public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {

                CommonResult commonResult;

                int count = brandService.updateBrand(id, pmsBrandDto);

                if (count == 1) {

                    commonResult = CommonResult.success(pmsBrandDto);

                    LOGGER.debug("updateBrand success:{}", pmsBrandDto);

                } else {

                    commonResult = CommonResult.failed("操作失敗");

                    LOGGER.debug("updateBrand failed:{}", pmsBrandDto);

                }

                return commonResult;

            }

         

            @Operation(summary = "刪除指定id的品牌")

            @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)

            @ResponseBody

            @PreAuthorize("hasRole('ADMIN')")

            public CommonResult deleteBrand(@PathVariable("id") Long id) {

                int count = brandService.deleteBrand(id);

                if (count == 1) {

                    LOGGER.debug("deleteBrand success :id={}", id);

                    return CommonResult.success(null);

                } else {

                    LOGGER.debug("deleteBrand failed :id={}", id);

                    return CommonResult.failed("操作失敗");

                }

            }

         

            @Operation(summary = "分頁(yè)查詢品牌列表")

            @RequestMapping(value = "/list", method = RequestMethod.GET)

            @ResponseBody

            @PreAuthorize("hasRole('ADMIN')")

            public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")

                                                                @Parameter(description = "頁(yè)碼") Integer pageNum,

                                                                @RequestParam(value = "pageSize", defaultValue = "3")

                                                                @Parameter(description = "每頁(yè)數(shù)量") Integer pageSize) {

                List<PmsBrand> brandList = brandService.listBrand(pageNum, pageSize);

                return CommonResult.success(CommonPage.restPage(brandList));

            }

         

            @Operation(summary = "獲取指定id的品牌詳情")

            @RequestMapping(value = "/{id}", method = RequestMethod.GET)

            @ResponseBody

            @PreAuthorize("hasRole('ADMIN')")

            public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {

                return CommonResult.success(brandService.getBrand(id));

            }}

        6.與SpringSecurity的結(jié)合

        如果項(xiàng)目中使用了SpringSecurity,需要做兩個(gè)配置來(lái)讓springdoc正常使用:

        1.在SpringSecurity配置類中放行白名單:"/v3/api-docs/**", "/swagger-ui/**"

        2.在SpringDoc配置中增加對(duì)應(yīng)內(nèi)容,如下:

        @Configurationpublic class SpringDocConfig {

         

            private static final String SECURITY_SCHEME_NAME = "BearerAuth";

         

            @Bean

            public OpenAPI managerOpenAPI() {

         

                return new OpenAPI()

                        .info(new Info().title("Galaxy-Cluster-Manager后端接口文檔")

                                .description("提供給前端界面(portal)的接口文檔")

                                .version("v1.0.0")

                                .license(new License().name("galaxy 1.2.0").url("https://gitlab.ctyun.cn/hpc/galaxy-parent/-/tree/v1.2.0")))

                        .externalDocs(new ExternalDocumentation()

                                .description("彈性高性能計(jì)算(CTHPC)")

                                .url("http://www.ctyun.cn"))

                        //以下是針對(duì)SpringSecurity的設(shè)置,同時(shí)還有設(shè)置白名單

                        .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))

                        .components(new Components()

                                .addSecuritySchemes(SECURITY_SCHEME_NAME,

                                        new SecurityScheme()

                                                .name(SECURITY_SCHEME_NAME)

                                                .type(SecurityScheme.Type.HTTP)

                                                .scheme("bearer")

                                                .bearerFormat("JWT")));

            }

            @Bean

            public GroupedOpenApi publicApi() {

                return GroupedOpenApi.builder()

                        .group("portal")

                        .pathsToMatch("/api/**")

                        .build();

            }

        }

        7.SpringDoc使用對(duì)象作為Query參數(shù)的問(wèn)題

        實(shí)際上springfox也會(huì)有這個(gè)問(wèn)題,使用對(duì)象作為query傳參的時(shí)候,頁(yè)面通常是這樣的:

        image.png 

        就沒(méi)有辦法逐個(gè)描述參數(shù),也不能逐個(gè)調(diào)試(只能用json調(diào)試),非常的麻煩;springdoc有一個(gè)解決這個(gè)問(wèn)題非常方便的注解:@ParameterObject

        比如某一個(gè)控制器的入?yún)⑹?/span>User user,我們只需要在這前面加上注解變?yōu)椋篅ParameterObject User user 即可,結(jié)果如下;

        image.png 

        這里也有一個(gè)大坑:參數(shù)的類型可能會(huì)不正確,比如這里我們的id參數(shù)實(shí)際上是int,但顯示出來(lái)是string,這個(gè)時(shí)候就需要我們?cè)赨ser類的屬性中加上對(duì)應(yīng)的注解,比如:

        @Parameter(description = "id傳參",example = "6")

        再重啟UI就會(huì)發(fā)現(xiàn)參數(shù)類型正確了

        8.SpringDoc配置掃包范圍

        有的時(shí)候僅僅使用@Hidden并不能滿足我們的需要,因?yàn)榭赡苄枰渲貌煌琯roup的controller類,這個(gè)時(shí)候就需要在配置類中取設(shè)置掃包范圍代碼如下:

        image.png 

        9.SpringDoc的優(yōu)劣勢(shì)

        優(yōu)勢(shì):SpringDoc有著非常好看的UI,以及比Springfox更加完善的參數(shù)注解體系,看起來(lái)非常舒服,并且還在不斷更新與維護(hù)中

        劣勢(shì):一些冷門(mén)功能還不完善,比如:

        a.有十個(gè)接口,他們的url是一樣的但是可以通過(guò)query參數(shù)來(lái)分別(如:@PostMapping(params = "action=QueryUsers"))這個(gè)時(shí)候springdoc只能通過(guò)掃包范圍配置,來(lái)寫(xiě)多個(gè)GroupOpenApi來(lái)解決,非常的麻煩;springfox可以在docket創(chuàng)建的時(shí)候使用:docket.enableUrlTemplating(true); 這個(gè)方法即可解決

        b.springdoc的網(wǎng)絡(luò)配置可能會(huì)與springfox沖突,如果遷移,需要逐個(gè)嘗試網(wǎng)絡(luò)配置是否合適(主要是GsonHttpMessageConverter的配置)

        c.兼容性問(wèn)題仍需要觀望,相對(duì)于springfox,springdoc的兼容性并沒(méi)有那么好,在許多時(shí)候可能會(huì)出現(xiàn)序列化的亂碼問(wèn)題

         

        總結(jié):如果當(dāng)前項(xiàng)目/工程已經(jīng)集成了完備的springfox,建議不要輕易嘗試遷移到springdoc,尤其是接口類型比較復(fù)雜、springfox配置docket比較多的項(xiàng)目;但如果是從頭開(kāi)始的項(xiàng)目,由于接口相對(duì)比較簡(jiǎn)單,可以采用springdoc,畢竟可以獲得更加清晰明了的顯示界面與參數(shù)解釋。

         


        *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



        關(guān)鍵詞: Java spring

        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 长治县| 安国市| 舞阳县| 普兰县| 陆丰市| 疏勒县| 扶沟县| 凤冈县| 交城县| 明光市| 桃江县| 黎平县| 闻喜县| 大石桥市| 汉寿县| 万安县| 达拉特旗| 巴彦县| 墨玉县| 扎囊县| 株洲县| 浦江县| 广河县| 荆州市| 涿鹿县| 佛坪县| 尚志市| 子长县| 富锦市| 延安市| 茌平县| 任丘市| 临沭县| 保山市| 台东县| 呼玛县| 五大连池市| 郓城县| 日照市| 昭平县| 普宁市|