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 构建运行时,将生成最新文档,该文档将始终与你的服务一致。只需将此文档发布给服务的使用者/客户。
快乐的编码。