Swagger2集成Springboot并支持泛型

    手头有几个rest服务的项目,写api文档是个力气活,更新api的时候文档经常更新不及时,被QA和使用者抱怨的厉害。一开始改成了markdown,起码可以放在git里方便维护,后来发现连markdown维护也麻烦。后来又尝试了yapi,对api使用者倒是很友好,一个一个api的网上加实在受不了。

花了点时间试用了一下Swagger,完了再配上yapi每分钟一次从dev分支的部署环境同步swagger文档,代码提交触发流水线自动部署,1分钟以后,其他人就能通过yapi查看和试用了。

马克一下,趟了几个小坑。主要是很多服务的输出都有类似的结构,但是,有一部分又不一样的情况,如分页的结果。文档主要是给用户看的,总不能给人家整个{"code": 0, "message": "haha", "result": {}}这样,让人家自己猜result里面都有哪些属性吧。

最后在官方实例中找到了解。

http://springfox.github.io/springfox/docs/current/#springfox-spring-mvc-and-spring-boot

pom.xml

最开始用了swagger2版本是2.8,结果不知道咋回事,浏览器死活js报错。各种查证都不行,最后,,改了2.5,莫名奇妙而好了。

    <properties>
        <spring-boot-version>2.0.0.RELEASE</spring-boot-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>${spring-boot-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${spring-boot-version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.5.0</version>
            </dependency>
        </dependencies>

 

增加两个Config类

 

Swagger2Config.java

import com.fasterxml.classmate.TypeResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import static springfox.documentation.schema.AlternateTypeRules.newRule;

@Configuration
@EnableSwagger2
public class Swagger2Config extends WebMvcConfigurationSupport {
    @Autowired
    private TypeResolver typeResolver;

    @Bean
    public Docket productApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
                .paths(PathSelectors.any())
                .build()
                .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class) //不需要在文档上显示细节的复杂类型
                .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
                .directModelSubstitute(java.time.ZonedDateTime.class, java.util.Date.class)
                .directModelSubstitute(JsonNode.class, Object.class)
                .apiInfo(metaData())
                .alternateTypeRules( //自定义规则,如果遇到DeferredResult,则把泛型类转成json
                        newRule(typeResolver.resolve(DeferredResult.class,
                                typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
                                typeResolver.resolve(WildcardType.class)))
                ;
    }
    private ApiInfo metaData() {
        return new ApiInfoBuilder()
                .title("REST API")
                .description("\"REST API for Online Store\"")
                .version("1.0.0")
                .license("Apache License Version 2.0")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0\"")
                .contact(new Contact("myname", "", "mail@mail"))
                .build();
    }
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

}

SpringSecConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SpringSecConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
 httpSecurity.authorizeRequests().antMatchers("/","/swagger-resources").permitAll();
 httpSecurity.csrf().disable();
 httpSecurity.headers().frameOptions().disable();
 }
}
 

MyController.java

@RestController
@RequestMapping("/service/v1")
@Api(value="Controller",tags={"接口"})
public class MyController {

    @ApiOperation(value="获得某个详细信息")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name="mnftCode", value="厂商", paramType="query"),
                    @ApiImplicitParam(name="badgeNmb", value="编号", paramType="query")
            }
    )
    @RequestMapping(value = "/meters/{mnftCode}/{badgeNmb}", method = RequestMethod.GET)
    public DeferredResult<ResponseEntity<BaseResponse<MeterVo>>> queryOneGasMeter(@PathVariable("mnftCode") String mnftCode,
                                                                                     @PathVariable("badgeNmb") String badgeNmb){
        DeferredResult<ResponseEntity<BaseResponse<MeterVo>>> result = new DeferredResult();
        if (mnftCode != null && badgeNmb != null){
            try {
                MeterVo meterVo = xxService.queryOneMeter(mnftCode,badgeNmb);
                result.setResult(BaseResponse.generateOKResponseEntity(meterVo));
            }catch (MyException e){
                result.setErrorResult(BaseResponse.generateBadResponseEntity(e.getErrorCode(), e.getMessage(), null));
            }
        }else {
            result.setErrorResult(BaseResponse.generateBadResponseEntity("error", null));
        }
        return result;
    }
}

BaseResponse.java

import io.swagger.annotations.ApiModelProperty;
import org.springframework.http.ResponseEntity;

public class BaseResponse<T> {
    @ApiModelProperty(value = "返回码:正确0, 警告2,错误为自定义码")
    private int responseCode;
    @ApiModelProperty(value = "返回消息")
    private String responseMsg;
    @ApiModelProperty(value = "返回具体内容")
    private T result;

    public BaseResponse(int responseCode, String responseMsg, T result) {
        this.responseCode = responseCode;
        this.responseMsg = responseMsg;
        this.result = result;
    }
    //这两个方法写在这里纯粹为了省地儿 
    public static ResponseEntity generateOKResponseEntity(Object object) {
        return ResponseEntity.ok().body(new BaseResponse<>(ResponseCode.CODE_SUCCESS, "Success", object));
    }

    public static ResponseEntity generateBadResponseEntity(String message, Object object) {
        return ResponseEntity.badRequest().body(
                new BaseResponse<>(ResponseCode.CODE_ERROR, message, object));
    }

    public static ResponseEntity generateBadResponseEntity(int code, String message, Object object) {
        return ResponseEntity.badRequest().body(
                new BaseResponse<>(code, message, object));
    }

    public int getResponseCode() {
        return responseCode;
    }

    public void setResponseCode(int responseCode) {
        this.responseCode = responseCode;
    }

    public String getResponseMsg() {
        return responseMsg;
    }

    public void setResponseMsg(String responseMsg) {
        this.responseMsg = responseMsg;
    }

    public T getResult() {
        return result;
    }
}

MeterVo的类定义上,也需要标注@ApiModelProperty注释

如此定义完毕,在swagger页面上,对此服务的出参,大概就会是下面这个样子。也比较适合统一有分页格式的输出。

{
  "responseCode": 0,
  "respponseMessage": "string",
  "result": {
      "mnftCode": "string",
      "badgeNmb": "string"
  }
}

 

 

 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值