[Day - 26] - Spring Swagger之我的SeaFood API 手册配制方法

Abstract

我们前面讲了许多Spring应用开发,但当我们开发好一套系统,势必要有一套API手册,不然前端开发者就会很辛苦的难以开发,都要等待您的API出来才能测试及开发,有没有觉得小编像罗志祥,转角遇到爱,等等!不是那个SWAG,我是威斯丁,小编其实是转角遇到金光党,你要怎麽整合金光党的包装呢?你也是要等它出完招数,以後你才知道怎麽测试与整合金光党吗!对不对!我好无言!写前端都会先刻假GUI,有的後端工程师就会看到,突然,好喔,那我配合你,那这样更累,这就是人生,充满了蜜罐,他还很开心,哎~不对,今天不讲资安,所以今天小编就把这个欧巴桑卖鱼系统,透过大家悉知的Swagger来做一个简易的API手册,让小编来跟大家说如何把API手册做的详细、做的快速,本日章节将提出如何配置个Swagger专有的说明注解(ApiOperation、ApiResponses),亦可透过Swagger操作手册进行触发个项API进行测试,以便提供前端开发者了解API的回覆结构及运作方法,可提高前後端分工效率,如此一来,我们就拥有一个系统规格的讨论工具罗!我是小编威斯丁,带你一同去探讨SWAGGER!等等!想到这边,我忘记说其实我上一篇是想推荐给我前区块链公司用的,但那时可能太忙我忘了,身为开发者在区块链的你,推荐你用那架构,效能特优的!我是小编威斯丁!现在就开始!

Principle Introduction

Swagger 目前只有提供在JAX-RS、Spring WEB REST、Jersey等专案的整合,Springfox 是一个将 Swagger 封装为可以整合至基於 Spring 生态系统的套件,并且提供了 springfox-swagger-ui 将 Swagger UI 整合至 Server後端,为一种服务器转导(SSR,Server Side Render),故透过Spring Boot 启动後,就可以直接进入 Swagger UI 的操作页面。相关程序码配置如下,请参考。

配置Swagger WebMvc 配置组态档

@Configuration
@EnableWebMvc
public class SwaggerWebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //预设拦截路径
        registry.addMapping("/**")
                //表示允许网域发发起哪些请求,这里表示支援HEAD,GET,PUT,POST,DELETE,PATCH
                .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //Swagger路径映射配置
        registry.addRedirectViewController("/docApi/v2/api-docs", "/v2/api-docs");
        registry.addRedirectViewController("/docApi/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
        registry.addRedirectViewController("/docApi/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
        registry.addRedirectViewController("/docApi/swagger-resources", "/swagger-resources");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 新增Swagger对映路径
        registry.addResourceHandler("/docApi/swagger-ui.html**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
        registry.addResourceHandler("/docApi/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

配置系统服务说明及相关API 触发测试配置内容

@Configuration
@EnableSwagger2
@EnableWebMvc
@Component
public class SwaggerConfig {

    @Value("${server.servlet.context-path}")
    String serviceBasePath;

    @Bean
    public Docket apiDefault() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("sw.spring.sample.soa"))
                .paths(PathSelectors.any())
                .build()
                .enable(true)
                .consumes(Sets.newHashSet(MediaType.APPLICATION_JSON_VALUE))
                .produces(Sets.newHashSet(MediaType.APPLICATION_JSON_VALUE));
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("WEISTING's Seafood RESTful API Information")
                .description("Definition of the Asia area Seafood product Retailer from Taiwan and China.")
                .license("Darius Weisting")
                .version("1.0")
                .contact(new Contact("Darius Weisting",null,"[email protected]"))
                .build();
    }
}

依据各API控制器群组名称,及配置API功能名称与叙述,并设置各种RESTful API的回覆状态内容叙述。

@Api(tags = "Taiwan Area Retailer API")
@RestController
public class ProductController extends ControllerBase{
    @Resource(name="seaFoodRetailService",type = SeaFoodRetailerService.class)
    SeaFoodRetailerService seaFoodRetailService;

    Logger logger = LoggerFactory.getLogger(ProductController.class);

    @GetMapping(
            value="/${sea.food.api.taiwan}/list",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ApiOperation(value = "Returns list sea food products", notes = "Returns a list sea food products base on GET Method.", response = List.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successful retrieval of sea food list products ! ", response = List.class),
            @ApiResponse(code = 400, message = "Sea food resource not found ! "),
            @ApiResponse(code = 500, message = "Internal server error") })
    ResponseEntity<Flux> listSeaFood() {
        return new ResponseEntity<>(Mono.just(seaFoodRetailService.listSeaFoodProducts()).flatMapMany(Flux::fromIterable),HttpStatus.OK);
    }
    
    @GetMapping(
            value="/${sea.food.api.taiwan}/find/{id}",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ApiOperation(value = "Returns sea food product by id ", notes = "Returns a list sea food product by id base on GET Method.", response = SeaFood.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successful retrieval of sea food products by id! ", response = SeaFood.class),
            @ApiResponse(code = 400, message = "Sea food resource not found ! "),
            @ApiResponse(code = 500, message = "Internal server error") })
    ResponseEntity<SeaFood> findSeaFoodById(@PathVariable("id") String id) throws ResourceNotFoundException {
        Optional<SeaFood> seaFood =  seaFoodRetailService.findProductById(id);
        if (!seaFood.isPresent())
            throw new ResourceNotFoundException();
        try {
            return new ResponseEntity<SeaFood>(
                    this.testAsyncAnnotationForMethodsWithReturnSeaFood(LocationEnum.TAIWAN,id)
                    , HttpStatus.OK
            );
        } catch (InterruptedException e) {
            logger.error("InterruptedException fail : {}" , e.toString());
            throw new ResourceNotFoundException();
        } catch (ExecutionException e) {
            logger.error("ExecutionException fail : {}" , e.toString());
            throw new ResourceNotFoundException();
        }

    }

    @PostMapping(
            value="/${sea.food.api.taiwan}/create",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ApiOperation(value = "Returns sea food product entity", notes = "Returns a sea food product by id base on request body.", response = List.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successful retrieval of sea food product ! ", response = SeaFood.class),
            @ApiResponse(code = 400, message = "Sea food resource not found ! "),
            @ApiResponse(code = 500, message = "Internal server error") })
    ResponseEntity<SeaFood> createSeaFood(@RequestBody SeaFood entity) throws SeaFoodRetailerGenericException {

        return new ResponseEntity<>(
             seaFoodRetailService.createSeaFood(entity)
                ,HttpStatus.CREATED
        );
    }
  .....
  .....
  .....
}

定义模型叙述

@ApiModel(description = "海鲜模型")
public class SeaFood {
    @ApiModelProperty(value = "海鲜序号", required = true)
    String id;
    @ApiModelProperty(value = "海鲜名称", required = true)
    String name;
    @ApiModelProperty(value = "海鲜叙述", required = true)
    String description;
    @ApiModelProperty(value = "海鲜价格", required = true)
    int price;

透过以上程序码范例,各位开发者各做参考,实作一份你自己的API Spec 吧

Tutorial Result

透过下图可看出API系统介绍与联系人资讯,且可看到小编定义的控制器名称,并列出内部API所有描述的资讯内容及方法。
image

下图可看到与小编定义的模型内容相同
image

透过下图可看出透过Swagger 页面上触发API,并确认可成功取得回覆资讯喔。
image

Sample Source

spring-sample-springfox

Reference Url

使用 Springfox 导入Swagger 3.0.0

tabnine-Cors-Registry-addMapping


<<:  TailwindCSS 从零开始 - 如何在 Angular 中使用 TailwindCSS

>>:  第六章 之七

[Day12] 从 function 谈变数的 Scope

说到 function ,又要回头来谈变数在 function 的 scope(作用域) 先宣告一个...

Day44. 范例:文字积木 (蝇量模式)

本文同步更新於blog 情境:这是公司生产的文字积木 <?php namespace Ap...

Day 13 - Slide In on Scroll

前言 JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用...

Day 22 | 使用相机获取影像

相机 可以使用Intent的方式(可复习Activity内容),开启装置上的相机应用程序获取影像,回...

从 IT 技术面细说 Search Console 的 27 组数字 KPI (22) :KPI 总表,项目流程次序

从 IT 技术面细说 Search Console 的 27 组数字 KPI (22) :KPI 总...