使用Spring REST Docs創建API文檔

使用Spring REST Docs創建API文檔

(這是在佳都科技實習時導師安排的學習整理的筆記。)

PS:可參考官網:https://spring.io/projects/spring-restdocs

1.創建項目 SpringBoot (maven)

首先創建一個SpringBoot的maven工程。創建好基本的controller層。

pom.xml

項目結構

2.加入對Spring REST Docs的支持

依賴

		<!--單元測試-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!--Spring REST Docs-->
        <dependency>
            <groupId>org.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-mockmvc</artifactId>
            <version>2.0.3.RELEASE</version>
            <scope>test</scope>
        </dependency>

插件

<plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>1.5.8</version>
                <executions>
                    <execution>
                        <id>generate-docs</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html</backend>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.restdocs</groupId>
                        <artifactId>spring-restdocs-asciidoctor</artifactId>
                        <version>2.0.3.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>

3.編寫好controller層的Junit單元測試

BaseControllerTest.class
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class BaseControllerTest<T> {
    private Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];//獲取泛型T的類
    private List<FieldDescriptor> fieldDescriptors = new ArrayList<>();//泛型T的字段說明
    private List<FieldDescriptor> objectResultFieldDescriptors = new ArrayList<>();//返回結果的data爲Object的字段說明
    private List<FieldDescriptor> booleanResultFieldDescriptors = new ArrayList<>();//返回結果的data爲Boolean的字段說明
    private MockMvc mockMvc;
    @Rule
    public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
    @Autowired
    private WebApplicationContext context;
    @Autowired
    private ObjectMapper objectMapper;
    //定義接口文檔輸出的位置格式
    private final String documentLocation = "{ClassName}/{methodName}";

    /**
     * 初始化
     */
    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(documentationConfiguration(this.restDocumentation))
                .build();
        {
            for (Field declaredField : entityClass.getDeclaredFields()) {
                fieldDescriptors.add(fieldWithPath(declaredField.getName()).description(declaredField.getAnnotation(ApiModelProperty.class).value()));
            }
        }
        {
            objectResultFieldDescriptors.add(fieldWithPath("code").description("業務碼"));
            objectResultFieldDescriptors.add(fieldWithPath("msg").optional().type(JsonFieldType.STRING).description("業務描述"));
            objectResultFieldDescriptors.add(subsectionWithPath("data").description("數據"));//data存在子節點
        }
        {
            booleanResultFieldDescriptors.add(fieldWithPath("code").description("業務碼"));
            booleanResultFieldDescriptors.add(fieldWithPath("msg").optional().type(JsonFieldType.STRING).description("業務描述"));
            booleanResultFieldDescriptors.add(fieldWithPath("data").description("是否成功"));//data不存在子節點
        }
    }

    /**
     * 測試類通過自定義的List<Snippet>的來控制接口的數據
     * 打印請求結果,同時生成接口文檔
     *
     * @param resultActions
     * @param snippets
     * @return
     * @throws Exception
     */
    public MvcResult afterRequest(ResultActions resultActions, List<Snippet> snippets) throws Exception {
        //爲傳進來的snippets在前面加上返回結果的字段說明
        snippets.add(0, relaxedResponseFields(
                objectResultFieldDescriptors
        ));
        return resultActions
                .andDo(MockMvcResultHandlers.print())
                .andExpect(status().isOk())
                .andDo(
                        document(documentLocation,
                                preprocessRequest(prettyPrint()),
                                preprocessResponse(prettyPrint()),
                                snippets.toArray(new Snippet[0]))
                )
                .andReturn();
    }

    public MockMvc getMockMvc() {
        return mockMvc;
    }

    public List<FieldDescriptor> getFieldDescriptors() {
        return fieldDescriptors;
    }

    public List<FieldDescriptor> getObjectResultFieldDescriptors() {
        return objectResultFieldDescriptors;
    }

    public List<FieldDescriptor> getBooleanResultFieldDescriptors() {
        return booleanResultFieldDescriptors;
    }

    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }
}

UserControllerTest .class
public class UserControllerTest extends BaseControllerTest<User> {
    /**
     * 根據用戶id獲取用戶信息
     *
     * @throws Exception
     */
    @Test
    public void getById() throws Exception {
        List<FieldDescriptor> roleFieldDescriptors = new ArrayList<>();
        for (Field declaredField : Role.class.getDeclaredFields()) {
            roleFieldDescriptors.add(fieldWithPath(declaredField.getName()).description(declaredField.getAnnotation(ApiModelProperty.class).value()));
        }

        List<Snippet> snippets = new ArrayList<>();
        snippets.add(requestParameters(parameterWithName("id").description("用戶id")));
        snippets.add(relaxedResponseFields(
                beneathPath("data").withSubsectionId("data"),
                getFieldDescriptors()
        ));
        //如果返回的data.roles存在數據,可以這樣輸出到roles文件中
//        snippets.add(relaxedResponseFields(
//                beneathPath("data.roles").withSubsectionId("roles"),
//                roleFieldDescriptors
//        ));
        Long id = 1L;
        MvcResult mvcResult = afterRequest(
                getMockMvc().perform(MockMvcRequestBuilders.get("/user/getById")
                        .param("id", id.toString())),
                snippets
        );
    }
 }

4.運行測試類後會生成如下文件

5.adoc文件轉html文件

在main創建如下文件夾和文件(文件名任意)

內容

= API
:toc: left
:toclevels: 4
:toc-title: 接口目錄

[[user]]
== 1.用戶管理

[[user-getById]]
=== 根據用戶id獲取用戶信息

operation::UserControllerTest/getById[]

運行mvn package或點擊如下位置

6.結果顯示

成功後在如下文件夾有結果

結果顯示

7.總結

可能剛接觸,體驗感一般。需要在測試代碼中寫上關於字段說明的代碼纔會顯示字段信息。由於返回的字段多,這裏用了泛型+反射的方式


因爲項目用到了Swagger,所以通過反射拿到字段的註解說明來進行描述。
同時需要注意的是如果response的字段是一個Object(即存在子節點),可以通過如下方式獲取。

就先這樣吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章