> Hello,欢迎来到程序员社区。 今天聊一聊 SpringBoot整合Swagger,不用手撕接口文档啦,希望对大家有所帮助。
Java面试手册PDF下载:[点击下载最全Java面试手册](http://117.78.51.75/219-2)
写在前面
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单,非常好用哦。不过这里要提醒一下的是,项目发布生产环境的时候,记得关闭swagger,以防泄漏项目接口文档,被攻击。如果觉得写得挺好,请给点个关注和点个赞哦,嘿嘿嘿
准备
首先第一步要做的当然是创建一个只有web场景的新项目,然后引入swagger的依赖,这里的swagger-ui
提供了一个非常好看的可视化UI,帮助我们整理Controller的API,从而生成API文档。
dependency>
groupId>io.springfox/groupId>
artifactId>springfox-swagger2/artifactId>
version>2.9.2/version>
/dependency>
dependency>
groupId>io.springfox/groupId>
artifactId>springfox-swagger-ui/artifactId>
version>2.9.2/version>
/dependency>
配置
我们导入了swagger的依赖之后,当然是需要对swagger进行相关的配置,我们在Config包下创建一个SwaggerConfig
配置类,用它来对swagger进行相关的配置,配置类的具体内容如下。当然,你还可对对固定接口进行过滤,不过我这里就不演示那么复杂了,就举个简单的例子。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
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;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.dbc.ubiquity.Controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Ubiquity系统")
.description("Ubiquity系统 API 1.0.0")
.termsOfServiceUrl("填一个你的借口文档的地址,或者其他地址都可以")
.version("1.0")
.contact(new Contact("DengBoCong", "个人网站地址", "你的邮箱"))
.build();
}
}
经过这2步配置后,我们启动服务后,访问:http://localhost:8080/swagger-ui.html就完成了集成。在上面的basePackage("com.dbc.ubiquity.Controller")
,我们配置了Swagger会默认把所有Controller中的RequestMapping方法都生成API出来。
常用注解
-
@Api:修饰整个类,描述Controller的作用
-
@ApiOperation:描述一个类的一个方法,或者说一个接口
-
@ApiParam:单个参数描述
-
@ApiModel:用对象来接收参数
-
@ApiProperty:用对象接收参数时,描述对象的一个字段
-
@ApiResponse:HTTP响应其中1个描述
-
@ApiResponses:HTTP响应整体描述
-
@ApiIgnore:使用该注解忽略这个API
-
@ApiClass
-
@ApiError
-
@ApiErrors
-
@ApiParamImplicit
-
@ApiParamsImplicit
写Controller测试
我们知道上面的一些注解的含义之后呢,我们现在可以来写Controller来测试一下,不过这里要说明一下的是,我们没有集成数据库,所以我这里使用模拟数据,Spring已经内含了模拟数据类,即MockMvc
,首先我们先写两个实体类(这个实体类当然不能使用@Entity
来注释啦),具体内容如下:
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
public class Message {
private Long id;
@ApiModelProperty(value = "消息体")
private String text;
@ApiModelProperty(value = "消息总结")
private String summary;
private Date createDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summa编程电子书汇总ry;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", text='" + text + ''' +
", summary='" + summary + ''' +
", createDate=" + createDate +
'}';
}
}
public class User {
private Long id;
private String name;
private int age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后是Repository类,具体内容如下
import java.util.List;
public interface MessageRepository {
ListMessage> findAll();
Message save(Message message);
Message update(Message message);
Message updateText(Message message);
Message findMessage(Long idJava面试手册);
void deleteMessage(Long id);
}
@Service("messageRepository")
public class InMemoryMessageRepository implements MessageRepository {
private static AtomicLong counter = new AtomicLong();
private final ConcurrentMapLong, Message> messages = new ConcurrentHashMap>();
@Override
public ListMessage> findAll() {
ListMessage> messages = new ArrayList>(this.messages.values());
return messages;
}
@Override
public Message save(Message message) {
Long id = message.getId();
if (id == null){
id = counter.incrementAndGet();
message.setId(id);
}
this.messages.put(id, message);
return message;
}
@Override
public Message update(Message message) {
this.messages.put(message.getId(), message);
return message;
}
@Override
public Message updateText(Message message) {
Message msg = this.messages.get(message.getId());
msg.setText(message.getText());
this.messages.put(msg.getId(), msg);
return msg;
}
@Override
public Message findMessage(Long id) {
return this.messages.get(id);
}
@Override
public void deleteMessage(Long id) {
this.messages.remove(id);
}
}
然后我们编写一个对各个实体类进行相应格式的基础类,具体内容如下
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/*
* 通用相应对象
* */
@ApiModel(description = "响应对象")
public class BaseResultT> {
private static final int SUCCESS_CODE = 0;
private static final String SUCCESS_MESSAGE = "成功";
@ApiModelProperty(value = "响应码", name = "code", required = true, example = "" + SUCCESS_CODE)
private int code;
@ApiModelProperty(value = "响应消息", name = "msg", required = true, example = SUCCESS_MESSAGE)
private String msg;
@ApiModelProperty(value = "响应数据", name = "data")
private T data;
private BaseResult(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
private BaseResult() {
this(SUCCESS_CODE, SUCCESS_MESSAGE);
}
private BaseResult(int code, String msg) {
this(code, msg, null);
}
private BaseResult(T data) {
this(SUCCESS_CODE, SUCCESS_MESSAGE, data);
}
public static T> BaseResultT> success() {
return new BaseResult>();
}
public static T> BaseResultT> successWithData(T data) {
return new BaseResult>(data);
}
public static T> BaseResultT> failWithCodeAndMsg(int code, String msg) {
return new BaseResult>(code, msg, null);
}
public static T> BaseResultT> buildWithParam(ResponseParam param) {
return new BaseResult>(param.getCode(), param.getMsg(), null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static class ResponseParam {
private int code;
private String msg;
private ResponseParam(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static ResponseParam buildParam(int code, String msg) {
return new ResponseParam(code, msg);
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
thiJava面试手册s.msg = msg;
}
public int getCode() {
return code;
}
}
}
到这里,我们所有的工作包括配置和逻辑都已经完成了,接下来就是测试我们编写的代码是否会生效了,测试类的具体内容如下:
import org.aspectj.lang.annotation.Before;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
public class MessageControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
// @Before(value = "saveMessage()")
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
saveMessages();
}
@Test
public void saveMessage() throws Exception {
setup();
final MultiValueMapString, String> params = new LinkedMultiValueMap>();
params.add("text", "text");
params.add("summary", "summary");
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.post("/message")
.params(params)).andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
@Test
public void getAllMessages() throws Exception {
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/messages"))
.andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
@Test
public void getMessage() throws Exception {
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/message/6"))
.andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
@Test
public void modifyMessage() throws Exception {
final MultiValueMapString, String> params = new LinkedMultiValueMap>();
params.add("id", "6");
params.add("text", "text");
params.add("summary", "summary");
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.put("/message").params(params))
.andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
@Test
public void patchMessage() throws Exception {
final MultiValueMapString, String> params = new LinkedMultiValueMap>();
params.add("id", "6");
params.add("text", "text");
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.patch("/message/text").params(params))
.andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
@Test
public void deleteMessage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.delete("/message/6"))
.andReturn();
String mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/messages"))
.andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);
}
private v编程电子书汇总oid saveMessages() {
for (int i=1;i10;i++){
final MultiValueMapString, String> params = new LinkedMultiValueMap>();
params.add("id",""+i);
params.add("text", "text"+i);
params.add("summary", "summary"+i);
try {
MvcResult mvcResult= mockMvc.perform(MockMvcRequestBuilders.post("/message")
.params(params)).andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
接下来我们访问http://localhost:8080/swagger-ui.html
最后
Swagger2默认将所有的Controller中的RequestMapping方法都会暴露,然而在实际开发中,我们并不一定需要把所有API都提现在文档中查看,这种情况下,使用注解@ApiIgnore来解决,如果应用在Controller范围上,则当前Controller中的所有方法都会被忽略,如果应用在方法上,则对应用的方法忽略暴露API。注解@ApiOperation和@ApiParam可以理解为API说明,多动手尝试就很容易理解了。如果我们不使用这样注解进行说明,Swagger2也是有默认值的,没什么可说的试试就知道了。
> 时间不一定能证明很多东西,但是一定能看透很多东西。坚信自己的选择,不动摇,使劲跑,明天会更好。