SpringBoot工作日记(4)API文档框架 —— Swagger

在使用了SpringBoot之后,我们不再像SpringMVC那样使用JSP了,通常使用Controller做接口,然后前端页面使用Ajax访问接口来交互数据,将前后端进行分离。这样约定接口格式就成了重要的事情,如果是和外部开发对接,那么得出具接口文档,如果是内部使用的接口,那么使用swagger可以自动生成接口文档供前端进行查阅,并且可以直接做接口测试,十分的方便。

首先我们要在SpringBoot项目中引入Swagger2的依赖。

        <!-- swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

我的swagger.version = 2.7.0 ,可以根据SpringBoot版本选择其他版本。

引入依赖之后需要对swagger进行配置。

建一个config包,然后在里面创建一个Config类和一个序列化实现类



import static springfox.documentation.builders.PathSelectors.regex;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;


/**
 * @project 项目:springboot
 * @createDate 创建时间:2018/11/16 18:49
 * @function 作用 : swagger2 的配置
 */

@Configuration
@EnableSwagger2
// 只允许dev和test环境下访问swagger
@Profile({"dev","test"})
public class SwaggerConfig {


	@Bean
	public Docket platformApi() {

		// 添加默认返回状态
        // 公共返回状态
		List<ResponseMessage> publicResponse = new ArrayList<ResponseMessage>();
		publicResponse.add(new ResponseMessageBuilder().code(200).message("成功").responseModel(new ModelRef("string")).build());
		publicResponse.add(new ResponseMessageBuilder().code(400).message("缺少参数").responseModel(new ModelRef("string")).build());
		publicResponse.add(new ResponseMessageBuilder().code(500).message("服务器内部错误").responseModel(new ModelRef("string")).build());
		publicResponse.add(new ResponseMessageBuilder().code(501).message("联系后端,验证类型写错了").responseModel(new ModelRef("string")).build());
		publicResponse.add(new ResponseMessageBuilder().code(502).message("请自行检查JSON格式").responseModel(new ModelRef("string")).build());
		publicResponse.add(new ResponseMessageBuilder().code(666).message("缺少token或token无效").responseModel(new ModelRef("string")).build());
        // get接口返回状态,继承自公共		
List<ResponseMessage> getResponse = publicResponse;
		getResponse.add(new ResponseMessageBuilder().code(201).message("查询成功但结果为空").responseModel(new ModelRef("string")).build());


		return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).forCodeGeneration(true)
				.select().apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
				.apis(RequestHandlerSelectors.any())
				.paths(regex("^.*(?<!error)$"))
				.build()
				.securitySchemes(securitySchemes())
				.securityContexts(securityContexts()).globalResponseMessage(RequestMethod.GET,getResponse)
				.globalResponseMessage(RequestMethod.PUT, publicResponse)
				.globalResponseMessage(RequestMethod.POST, publicResponse)
				.globalResponseMessage(RequestMethod.DELETE, publicResponse);


	}


	private List<ApiKey> securitySchemes() {
		List<ApiKey> apiKeyList= new ArrayList();
		//指定token的存放位置
		apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
		return apiKeyList;
	}

	private List<SecurityContext> securityContexts() {
		List<SecurityContext> securityContexts=new ArrayList<>();
		securityContexts.add(
				SecurityContext.builder()
						.securityReferences(defaultAuth())
						.forPaths(regex("^(?!auth).*$"))
						.build());
		return securityContexts;
	}

	//自动验证
	List<SecurityReference> defaultAuth() {
        // 指定需要验证信息的域,这里整个文档接口都需要校验
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		List<SecurityReference> securityReferences=new ArrayList<>();
		securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
		return securityReferences;
	}


    
	private ApiInfo apiInfo() {
        // 指定文档标题和版本号
		return new ApiInfoBuilder().title("xxxx项目在线API文档").version("1.0").build();
	}

}

在apiInfo方法中,指定文档的标题和版本号,效果如下图

在platformApi方法中,我指定了一些返回码的含义,效果如下图。

securitySchemes和securityContexts还有defaultAuth三个方法是在接口配置了拦截器校验时使用的。

例如我要求请求中header的Authorization要携带用户名密码或者token之类的(上面代码是带token),若不进行配置的话,swagger只是发个普通的请求而已,会被我们的filter拦截,这样就只能看文档,不能直接进行测试了。

所以像上面那样配置之后,swagger右上角会有个按钮用来输入验证信息

我们输入有效的token或者用户名密码之后,每个接口旁边的红色感叹号就会消失了。当然securitySchemes方法中可以增加更多的输入,我就不演示了。

然后是一个序列化配置,就是序列化而已,没什么好说的。



import java.io.IOException;
import java.lang.reflect.Type;

import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;

import springfox.documentation.spring.web.json.Json;

/**
 *
 */
public class SwaggerJsonSerializer implements ObjectSerializer, ObjectDeserializer {

    public final static SwaggerJsonSerializer instance = new SwaggerJsonSerializer();

    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        SerializeWriter out = serializer.getWriter();
        Json json = (Json) object;
        out.write(json.value());
    }

    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        return null;
    }

    @Override
    public int getFastMatchToken() {
        return 0;
    }

}

接下来我们看接口怎么写。



import java.util.Date;
import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import com.dingguan.shanyu.bean.ResultBean;
import com.dingguan.shanyu.util.Sequences;
import com.dingguan.shanyu.web.comment.pojo.Comment;
import com.dingguan.shanyu.web.comment.pojo.CommentBean;
import com.dingguan.shanyu.web.comment.service.CommentService;
import com.github.pagehelper.Page;

import io.swagger.annotations.*;

/**
 * @project 项目:springboot
 * @createDate 创建时间:2019/1/3 17:06
 * @function 作用 : 评论 的对外接口
 */

@RestController
@RequestMapping("/Comment")
@Api(description = "评论")
public class CommentController {

	@Autowired CommentService service;

	@PostMapping
	@ApiOperation("评论")
	@ApiResponses({@ApiResponse(code = 400, message = "缺少参数")})
	@ApiImplicitParams({@ApiImplicitParam(name = "comment", value = "*", paramType = "body", dataType = "Comment")})
	public ResultBean insert(@RequestBody @Valid Comment comment) {
		//初始化参数
		comment.setId(Sequences.getId());
		comment.setTime(new Date());
		comment.setPraise(0);
		service.insert(comment);
		return new ResultBean(200, comment);
	}

	@GetMapping
	@ApiOperation("查询列表")
	@ApiResponses({})
	@ApiImplicitParams({
			@ApiImplicitParam(name="user",value="用户ID",paramType = "query",dataType = "string"),
			@ApiImplicitParam(name="objType",value="评论对象类型:0-帖子,1-评论",paramType = "query",dataType = "int"),
			@ApiImplicitParam(name="obj",value="对象ID",paramType = "query",dataType = "string"),
			@ApiImplicitParam(name="root",value="根对象",paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "index", value = "第几个", paramType = "query", dataType = "int"), @ApiImplicitParam(name = "size", value = "每页几个", paramType = "query", dataType = "int")})
	public ResultBean<List<Comment>> findList(String user,Integer objType,String obj,String root,Integer index, Integer size) {
		//验证参数
		if (index == null || size == null) {
			List<CommentBean> list = service.selectList(user,objType,obj,root);
			if(list==null|| list.isEmpty()){
				return new ResultBean(201);
			}
			ResultBean resultBean = new ResultBean(200,list);
			// List<replaceBean> beans = service.turnToBeans(list);
			// resultBean.setData(beans);
			return resultBean;
			// return new ResultBean(400);
		}
		Page<CommentBean> pages = service.selectList(user,objType,obj,root,index, size);
		List<CommentBean> list = pages.getResult();
		if (list == null || list.isEmpty()) {
			return new ResultBean(201);
		}
		//封装类
		// List<CommentBean> beans = service.turnToBeans(list);
		ResultBean resultBean = new ResultBean(200, list,index,size, pages.getTotal());
		// resultBean.setData(beans);
		return resultBean;
	}


	@DeleteMapping("/{id}")
	@ApiOperation("删除")
	@ApiResponses({@ApiResponse(code = 400, message = "缺少参数")})
	@ApiImplicitParams({@ApiImplicitParam(name = "id", value = "*", paramType = "path", dataType = "stirng")})
	public ResultBean deleteById(@PathVariable String id) {
		//验证参数	
		if (id == null) {
			return new ResultBean(400);
		}
		service.delete(id);
		return new ResultBean(200);
	}

	@PutMapping
	@ApiOperation("修改")
	@ApiResponses({@ApiResponse(code = 400, message = "缺少参数")})
	@ApiImplicitParams({@ApiImplicitParam(name = "comment", value = "*必须携带ID", paramType = "body", dataType = "Comment")})
	public ResultBean<Comment> update(@RequestBody Comment comment) {
		//验证参数	
		if (comment.getId() == null) {
			return new ResultBean(400);
		}
		service.update(comment);
		return new ResultBean(200, comment);
	}
}

首先接口是按照不同的controller类,分成了不同的接口组。

Controller类上面的@Api 是对接口组的一个说明

然后我们在写RequestMapping的时候要注意,尽量别使用RequestMapping,因为swagger会为RequestMapping默认生成GET、POST、PUT、DELETE等等一套接口,实际上我们只实现了一个接口,所以不要使用RequestMapping,而去直接使用GetMapping、PostMapping这样的注解。

@ApiOperation 是对这个接口的说明

@ApiResponses 是说明接口返回的状态码的含义。这里是个数组,可以指定多个返回码,如果在前面SwaggerConfig配置了公共的返回码也会有用,但是这里的优先。

@ApiResponse 是@ApiResponses的数组元素,code配置返回码,message配置返回码的含义。

@ApiImplicitParams 是说明接口的入参,这里是个数组,可以设置多个入参。

@ApiImplicitParam 是具体每个入参,name为参数名,value为参数的说明,dataType为常数的类型(可以是Java类,如果String提示错误就用string),paramType是指定参数所在位置(例如path就是restful风格,query是URL上传参,body就是放在请求体中,paramType是可以组合使用的),required 是设置参数是否必填(代码中我没使用)

@ApiIgnore 不在接口文档显示

具体大家自己尝试过才清楚。

还有一点就是它会自己解析接口返回体的格式,我们可以在Response Class里看到,model是类格式,example value是举例

这里返回体的字段注释是怎么配的呢?

如果我们返回String的话就不用配了,我们只能设置自己可以修改的类。

从我的Controller可以看出我是封装了一个ResultBean



import com.dingguan.shanyu.constants.Constants;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * @createDate 创建时间:2018年9月22日 下午5:56:36
 * @function 作用: 返回小程序和web的结果实体类
 */
@Data
@ApiModel
public class ResultBean<T> {

	@ApiModelProperty("状态码")
	private int code = Constants.RESP_STATUS_OK;

	@ApiModelProperty("消息")
	private String message;

	@ApiModelProperty("数据")
	private T data;

	@ApiModelProperty("总条数")
	private Long total;

	@ApiModelProperty("每次查询数量")
	private Integer size;

	@ApiModelProperty("第几条开始")
	private Integer index;

	public ResultBean() {
		this.setCode(200);
	}

	// 封装消息
	public ResultBean(int code, String message) {
		this.code = code;
		this.message = message;
	}

	// 封装消息
	public ResultBean(int code, T data, String message) {
		this.code = code;
		this.data = data;
		this.message = message;
	}

	// 封装消息
	public ResultBean(int code, T data) {
		this.code = code;
		this.data = data;
	}

	// 封装消息
	public ResultBean(int code) {
		this.code = code;
	}

	// 封装数据
	public static ResultBean returnData(Object o) {
		ResultBean result = new ResultBean();
		result.setCode(200);
		result.setData(o);
		return result;
	}

	//初始化数据
	public ResultBean(int code, T data, Integer index, Integer size,Long toatl){
		this.code = code;
		this.data = data;
		this.index = index;
		this.size = size;
		this.total = toatl;
	}

}

加了@ApiModel 之后Swagger 才能解析,然后每个字段上面用@ApiModelProperty加字段注释。

如果像我的Get接口一样是返回list的话它也会自己处理。如果加了泛型,会泛型也一样配置@ApiModel就可以。

项目启动之后,在项目访问路径后面加 /swagger-ui.html 就可以打开文档页面了。在里面输入参数之后,点击Try it out !就会向接口发送请求。

如上图是paramType为query的方式,可以看到CURL和request headers 还有respongse body,一目了然。

 

补充说明,在SwaggerConfig的platformApi中设置默认返回状态时,

new ModelRef() 可以为 new ModelRef(null) ,但这样在新版本中会报错,所以最后默认为new ModelRef("string")

当然,也可以是自定的类,和ApiImplicitParam 的dataType是类似的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值