API 文件示例
使用 Spring REST Docs 來記錄你的服務。它是一個功能強大的框架,可確保服務邏輯始終與文件內聯。為此,你必須為你的服務編寫整合測試。
如果文件和服務行為不匹配,測試將失敗。
以下是在 maven 專案中生成文件的示例示例:
在 pom 檔案中新增此依賴項:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<version>2.6.6.RELEASE</version>
</dependency>
此外,新增 ASCII 文件外掛以生成 build.puglin 標記下的文件
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
</dependencies>
</plugin>
現在,作為示例,讓我們建立一個我們想要記錄的控制器服務。
package com.hospital.user.service.controller;
import org.springframework.hateoas.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.hospital.user.service.entity.User;
import com.hospital.user.service.exception.UserNotFoundException;
import com.hospital.user.service.repository.UserCrudRepository;
import com.hospital.user.service.resource.assembler.UserResourceAssembler;
@Controller
@RequestMapping("/api/user")
public class SampleController {
final UserCrudRepository userRepository;
final UserResourceAssembler userResourceAssembler;
final BCryptPasswordEncoder passwordEncoder;
public SampleController(UserCrudRepository userCrudRepository, UserResourceAssembler userResourceAssembler, BCryptPasswordEncoder passwordEncoder){
this.userRepository = userCrudRepository;
this.userResourceAssembler = userResourceAssembler;
this.passwordEncoder = passwordEncoder;
}
@RequestMapping(method = RequestMethod.GET, value = "/{userId}", produces = { MediaType.APPLICATION_JSON_VALUE})
ResponseEntity<Resource<User>> getUser(@PathVariable String userId){
User user = (User) this.userRepository.findOne(userId);
if(user==null){
throw new UserNotFoundException("No record found for userid"+ userId);
}
Resource<User> resource = this.userResourceAssembler.toResource(user);
return new ResponseEntity<Resource<User>>(resource, HttpStatus.OK);
}
}
讓我們編寫測試用例來測試這個服務:
package com.hospital.user.service;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@EnableWebMvc
@ComponentScan( basePackages = { "com.hospital.user.service" } )
@SpringBootTest
public class SampleControllerTest {
private RestDocumentationResultHandler documentationHandler;
@Rule public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
@Autowired private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp(){
this.documentationHandler = document("{method-name}", //Documents would be generated by the test method name.
preprocessRequest(prettyPrint()), //To print request
preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)
.uris()
//.withScheme("https") Specify this for https
.withHost("recruitforceuserservice") //To use the hostname
.withPort(8443)
)
.alwaysDo(this.documentationHandler)
.build();
}
@Test
public void getUser() throws Exception {
// tag::links[]
this.mockMvc.perform(get("/api/user/"+"591310c3d5eb3a37183ab0d3").header("Authorization",
"Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJiaGFyZHdhai5uaXRpc2gxOSIsInJvbGVzIjpbIkFETUlOIl0sImlzcyI6Imh0dHA6Ly9ob3NwaXRhbC1jbG91ZC5jb20iLCJpYXQiOjE1MDExNTA3NDcsImV4cCI6MTUwMTE1MTY0N30.NPumYw9mslHdJjaaodsIzKX4y0Udbf2Co1kVOXBEaqNdEUIUSwgwQZn23TMWVsehudlwTu4KgCU2ZHXXccCDIQ"))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
responseHeaders(
headerWithName("Content-Type").description("The Content-Type of the payload: `application/json`")//Asserts that the response has this header.
),
responseFields(
fieldWithPath("username").description("Unique name for the record"), //Asserts that the response has this field.
fieldWithPath("password").description("password of the user"),
fieldWithPath("securityAnswer").description("Security answer which would be used to validate the user while the password is reset."),
fieldWithPath("securityQuestion").description("Security question to reset the password"),
fieldWithPath("email").description("Email of the user"),
fieldWithPath("roles").description("Assigned roles of the user"),
fieldWithPath("id").description("Unique identifier of an user"),
fieldWithPath("_links").ignored()
)
));
}
}
執行單元測試,在目標資料夾下生成一些檔案。
建立一個原始檔夾作為 src / main / asciidocs 並建立一個字首為 adoc 的 doc 檔案來記錄你的服務詳細資訊。示例 doc 檔案
[[resources]]
= Resources
User: Have the information about an User. It has following fields:
include::{snippets}/get-user/response-fields.adoc[]
[[resources-user]]
== User
The User resources has all the information about the application user. This resource is being used to perform all CRUD operations on User entity.
[[resources-user-retrieve]]
=== Retrieve User
A `GET` request gets a User.
operation::get-user[snippets='response-fields,curl-request,http-response']
doc 檔案中的 include 標記包含片段。你可以指定生成文件的任何格式。
完成所有操作後,執行 Maven Build 。它將執行你的測試,asciidoc 外掛將在 target.generate-docs 資料夾下生成你的文件 html 檔案。生成的檔案看起來像這樣:
每當你的 maven 構建執行時,將生成最新文件,該文件將始終與你的服務一致。只需將此文件釋出給服務的使用者/客戶。
快樂的編碼。