Swagger接口框架

一.简介

解决什么问题:

1)在前后台分离的开发模式中,减小接口定义沟通成本,方便开发过程中测试,自动生成接口文档。提高Java服务端和Web前端以及移动端的对接效率。

2)Swagger是当前最好用的Restful API文档生成的开源项目,通过swagger-spring项目实现了与SpingMVC框架的无缝集成功能,方便生成spring restful风格的接口文档,同时swagger-ui还可以测试spring restful风格的接口功能。

1、Swagger 包含的主要套件:

  • Swagger Editor - 基于浏览器的编辑器,用来编写 OpenAPI 规范。
  • Swagger UI - 基于 OpenAPI 规范动态生成 API 规范文档。
  • Swagger Codegen - 个模板驱动引擎,用来生成客户端代码。

2、OpenAPI是什么?

OpenAPI 规范,以前叫 Swagger 规范。它是一个为 REST API的接口定义的规范。

OpenAPI 可以定义的 API 实体内容包括以下几个部分。

  • 请求地址(如:/user)
  • 请求类型(如:GET、POST 等)
  • 请求参数
  • 响应参数
  • 验证方式
  • 文档信息:如联系人、许可证、服务条件等

二.Swagger配置及使用

1、对于一个SpringMVC项目,使用swagger的配置如下:pom.xml

<!-- Swagger -->  
<dependency>  
    <groupId>io.swagger</groupId>  
    <artifactId>swagger-core</artifactId>  
    <version>1.5.8</version>  
</dependency>  
<dependency>  
    <groupId>io.springfox</groupId>  
    <artifactId>springfox-swagger2</artifactId>  
    <version>2.4.0</version>  
</dependency>  
<dependency>  
    <groupId>io.springfox</groupId>  
    <artifactId>springfox-swagger-ui</artifactId>  
    <version>2.4.0</version>  
</dependency>  

2、SwaggerConfiguration.java

@Configuration  
@EnableSwagger2  
public class SwaggerConfiguration {  
    @Bean  
    public Docket api() {  
        return new Docket(DocumentationType.SWAGGER_2)  
                .select()  
                .apis(RequestHandlerSelectors.any())  
                .paths(PathSelectors.any())  
                .build();  
    }  
}  

3、SwaggerWebMvcConfigurerAdapter.java

@Configuration  
@EnableWebMvc  
@ComponentScan(basePackages = "com.xx.travel.csc.stat.controller")  
public class SwaggerWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {  
   
    @Bean  
    public ViewResolver viewResolver() {  
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();  
        viewResolver.setViewClass(JstlView.class);  
        viewResolver.setPrefix("/WEB-INF/views/");  
        viewResolver.setSuffix(".jsp");  
        return viewResolver;  
    }  
   
    @Bean  
    public MessageSource messageSource() {  
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();  
        messageSource.setBasename("messages");  
        return messageSource;  
    }  
    @Override  
    public void addResourceHandlers(ResourceHandlerRegistry registry) {  
        super.addResourceHandlers(registry);  
        registry.addResourceHandler("swagger-ui.html")  
                .addResourceLocations("classpath:/META-INF/resources/");  
        registry.addResourceHandler("/webjars/**")  
                .addResourceLocations("classpath:/META-INF/resources/webjars/");  
    }  
    @Override  
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {  
        configurer.enable();  
    }  
}  

4、Controller实例

然后,只要在我们的Controller里面增加注解 ApiOperation和ApiParam 即可。

@Controller  
@RequestMapping(value = "/stat")  
public class SwaggerController {  
       
    @ResponseBody  
    @RequestMapping(value = "/helloworld", method = RequestMethod.GET)  
    @ApiOperation(nickname = "swagger-helloworld", value = "Swagger的世界", notes = "测试HelloWorld")  
    public String helloWorld(@ApiParam(value = "昵称") @RequestParam String nickname) {  
        return "Hello world, " + nickname;  
    }  
       
    @ResponseBody  
    @RequestMapping(value = "/objectio", method = RequestMethod.POST)  
    @ApiOperation(nickname = "swagger-objectio", value = "Swagger的ObjectIO", notes = "测试对象输入输出")  
    public SwaggerOutput objectIo(@ApiParam(value = "输入") @RequestBody SwaggerInput input) {  
        SwaggerOutput output = new SwaggerOutput();  
        output.setId(input.getId());  
        output.setName("Swagger");  
        return output;  
    }  
}  

5、Web界面

启动项目,输入Http://Path:Post/swagger-ui.html,就可以给前端展示相关的API文档,并像使用Postman以及Curl命令一样,通过Web界面进行接口测试。

在Spring 4.3以后,引入了@GetMapping、@PostMapping、@PutMapping、@DeleteMapping和@PatchMapping,一共5个注解。

  • 请求资源应该使用GET;
  • 添加资源应该使用POST;
  • 更新资源应该使用PUT;
  • 删除资源应该使用DELETE.

三.Spring接口文档注解

1、@Api:用于controller上,表示标识这个类是swagger的资源 。

​@Api(tags = {"user"}, description = "Operations about user")
属性名称备注
valueurl的路径值
tags如果设置这个值、value的值会被覆盖
description对api资源的描述

2、@ApiOperation:用于方法上,说明方法的作用。

@ApiOperation(value="获取项目列表", notes="获取项目列表", httpMethod="POST")
属性名称备注
valueurl的路径值
notes对api资源的描述
httpMethod请求类型。GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" , "PATCH"

3、@ApiResponse:用于方法上,表示响应配置。

@ApiResponses({
            @ApiResponse(code = 200, message = "返回成功", response = Entity.class)
    })
属性名称备注
codehttp的状态码
message描述
response默认响应类 Void

4、@ApiResponses:用于方法上,表示响应集配置。

​@ApiResponses({ @ApiResponse(code = 400, message = "Invalid Order"),
    @ApiResponse(code = 200, message = "Success")
 })

5、@ApiImplicitParam:用于方法上,指定一个请求参数的信息。

@ApiImplicitParam(name = "id", value = "主键id", required = true, paramType = "query", dataType = "Int")
属性名称备注
name参数名
value参数说明
dataType参数类型(int、String)
paramType

参数放在哪个地方

1.header-->请求参数的获取:@RequestHeader(代码中接收注解)。

2.query-->请求参数的获取:@RequestParam(代码中接收注解)。

3.path(用于restful接口)-->请求参数的获取:@PathVariable(代码中接收注解)。

4.body-->请求参数的获取:@RequestBody(代码中接收注解)。

5.form(不常用)。

required是否必填
defaultValue参数的默认值
allowMultipletrue -- > 表示是数组格式的参数

6、@ApiImplicitParams:用于方法上,包含一组参数说明。

@ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "主键id", required = true, paramType = "query", dataType = "Int"),
            @ApiImplicitParam(name = "name", value = "名字", required = true, paramType = "query", dataType = "String")
    })

7、@ApiParam:用在方法参数前,表示api需要传递的参数。

@ApiParam(value = "接口说明", required = true)
属性名称备注
name属性名称
value属性值
defaultValue默认属性值
required是否必填
example示例

8、@ApiModel:用于类上,表示标识这个类是swagger的资源。

@ApiModel(value = "参数对象", description = "参数对象")

9、@ApiModelProperty:用于属性上,表示对model属性的说明或者数据操作更改。

@ApiModelProperty(value = "类型1", name = "type", example = "1", required = true)
属性名称备注
name属性名称
value属性说明
dataType参数类型
required是否必填
example示例

10、封装Map请求API

(1)创建ApiJsonObject.java

@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject {

    ApiJsonProperty[] value(); //对象属性值

    String name();  //对象名称
}

(2)创建ApiJsonProperty.java

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty {

    String key();  //key

    String example() default "";

    String type() default "string";  //支持string 和 int

    String description() default "";
}

(3)创建ApiJsonProperty.java

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.pactera.cloud.item.model.ApiJsonObject;
import com.pactera.cloud.item.model.ApiJsonProperty;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.util.Map;

@Component
@Order
public class MapApiReader implements ParameterBuilderPlugin {

    @Autowired
    private TypeResolver typeResolver;

    @Override
    public void apply(ParameterContext parameterContext) {
        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();

        if (methodParameter.getParameterType().canCreateSubtype(Map.class) || methodParameter.getParameterType().canCreateSubtype(String.class)) { //判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象
            Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);  //根据参数上的ApiJsonObject注解中的参数动态生成Class
            if (optional.isPresent()) {
                String name = optional.get().name();  //model 名称
                ApiJsonProperty[] properties = optional.get().value();

                parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name)));  //像documentContext的Models中添加我们新生成的Class

                parameterContext.parameterBuilder()  //修改Map参数的ModelRef为我们动态生成的class
                        .parameterType("body")
                        .modelRef(new ModelRef(name))
                        .name(name);
            }
        }

    }

    private final static String basePackage = "com.xx.xxx.in.swagger.model.";  //动态生成的Class名

    /**
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     */
    private Class createRefModel(ApiJsonProperty[] propertys, String name) {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(basePackage + name);

        try {
            for (ApiJsonProperty property : propertys) {
                ctClass.addField(createField(property, ctClass));
            }
            return ctClass.toClass();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据property的值生成含有swagger apiModelProperty注解的属性
     */
    private CtField createField(ApiJsonProperty property, CtClass ctClass) throws NotFoundException, CannotCompileException {
        CtField ctField = new CtField(getFieldType(property.type()), property.key(), ctClass);
        ctField.setModifiers(Modifier.PUBLIC);

        ConstPool constPool = ctClass.getClassFile().getConstPool();

        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
        ann.addMemberValue("value", new StringMemberValue(property.description(), constPool));
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(String.class.getName())))
            ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(Integer.class.getName())))
            ann.addMemberValue("example", new IntegerMemberValue(Integer.parseInt(property.example()), constPool));

        attr.addAnnotation(ann);
        ctField.getFieldInfo().addAttribute(attr);

        return ctField;
    }

    private CtClass getFieldType(String type) throws NotFoundException {
        CtClass fileType = null;
        switch (type) {
            case "string":
                fileType = ClassPool.getDefault().get(String.class.getName());
                break;
            case "int":
                fileType = ClassPool.getDefault().get(Integer.class.getName());
                break;
        }
        return fileType;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}

(4)示例

/*
     * 获取用户信息
     */
    @PostMapping("/getUserInfo")
    @ApiOperation(value="获取用户信息", notes="获取用户信息", httpMethod="POST")
    @ApiResponses({
            @ApiResponse(code = 200, message = "返回成功", response = TbProject.class)
    })
    public ResponseView getUserInfo(@ApiJsonObject(name = "user_info", value = {
            @ApiJsonProperty(key = "userName", example = "大王", description = "用户名称")
    }) @RequestBody Map<String,Object> param) {
        String userName= String.valueOf(param.get("userName"));
        return userService.getUserInfo(userName);
    }

四.问题汇总

1、Swagger-UI 提示Unable to infer base url

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值