Golden Recursion Inc. logoGolden Recursion Inc. logo
Advanced Search
Spring REST Docs

Spring REST Docs

It helps you to produce documentation that is accurate, concise, and well-structured. This documentation then allows your users to get the information they need with a minimum of fuss.

The aim of this extension to Spring REST Docs is to help you to write even less — both code and documentation. You still get the same nice documentation as with Spring REST Docs itself. The main benefit is that writing less and moving the documentation closer to the code increases the maintainability of the documentation.

In Spring REST Docs you have to add the documentation for your JSON with a DSL in your test. We moved this documentation to the POJO that represents your JSON object. You just add Javadoc to the fields and it will end up in the documentation.

Features:

  • Jackson visitor that gathers the whole JSON structure and includes Javadoc and constraint annotations on the fields. It works for both request and response bodies. In addition to the constraint documentation support that is already in Spring REST Docs, the constraint message is automatically included in the documentation. Constraint groups are also supported.
  • Path and query parameters can be documented automatically.
  • A helper to document authentication.
  • A snippet that includes all other snippets and thus helps you write even less.

FAQ

  • Does it work with Spring Web MVC tests?

Yes, take a look at the example.

  • Does it work with Spring WebFlux?

Yes, take a look at the example.

  • Is Kotlin supported?

Yes, Spring Auto REST Docs 1.0.13 and 2.0.0 introduced Kotlin support. Instead of the Javadoc Doclet one has to use the Dokka extension spring-auto-restdocs-dokka-json. The extension is only available as 2.0.x, but works with both version of Spring Auto REST Docs.

  • Which Java versions are supported?

The baseline is Java 8. Our focus is on the LTS versions 8 and 11, but we also try to support the latest Java release. One has to use a different Javadoc Doclet starting at Java 9. See Getting Started.

  • Does it work with REST Assured tests?

Not at the moment. If you want this, your PR is welcome.

  • Is Jackson required for automatic field documentation?

Yes, this project only includes a Jackson visitor so far.

  • Is a multi-module project setup supported?

The JSON doclet and the REST Docs snippets produce files. Therefore, all files should be placed on the filesystem. JSON files will be additionally looked up from classpath, so modularization of codebase is fully supported. For advanced customization, multiple source directories for the JSON files can be configured (see usage).

  • Can Spring REST Docs and Spring Auto REST Docs features be combined?

Yes, this even works in the same test. One can generate Spring REST Docs snippets and Spring Auto REST Docs snippets out of the same test. This is demonstrated in the example project.

  • Can I regenerate the final HTML without running all tests?

Yes, if you do not need the snippets generated out of the test to be updated, Asciidoctor can be executed standalone. With Maven you can run mvn asciidoctor:process-asciidoc (where process-asciidoc is the goal specified in your Maven POM and might differ). With Gradle it can be done via ./gradlew asciidoctor if asciidoctor is the name of the Gradle task in your setup. You might have to remove dependsOn test depending on your setup to run it standalone.

  • Is HATEOAS supported?

Yes, there is basic support for links and embedded resources.

  • What should I do if I get a NoSuchBeanDefinitionException: No bean named 'webHandler' available exception?

This means that the servlet stack instead if the reactive one is active. Make sure that @EnableWebFlux is used.

Getting started

Requirements

Spring Auto REST Docs has the following minimum requirements:

  • Java 8 or Kotlin 1.3
  • Spring REST Docs 2.0.5.RELEASE (see documentation)
  • Jackson has to be used for creating and parsing JSON

For Java 7 and Spring REST Docs 1.2.x support, please use the 1.0.x version of Spring Auto REST Docs.

For Java 9+ support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency. Our support is focused on the latest Kotlin version, Java 8 and 11. In addition, we try to support the latest Java version that is supported by Spring.

Usage

  • Setup project for Spring REST Docs

  • Additional configuration for this extension:

Maven

<dependency>
    <groupId>capital.scalable</groupId>
    <artifactId>spring-auto-restdocs-core</artifactId>
    <version>2.0.11</version>
    <scope>test</scope>
</dependency>

<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
             <configuration>
                <includes>
                    <include>**/*Test.java</include>
                </includes>
                <systemPropertyVariables>
                    <org.springframework.restdocs.outputDir>
                        ${project.build.directory}/generated-snippets
                    </org.springframework.restdocs.outputDir>
                    <org.springframework.restdocs.javadocJsonDir>
                        ${project.build.directory}/generated-javadoc-json 
                    </org.springframework.restdocs.javadocJsonDir>
                </systemPropertyVariables>
             </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-javadoc-plugin</artifactId>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <id>generate-javadoc-json</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>javadoc-no-fork</goal>
                    </goals>
                    <configuration>
                        <doclet>capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet</doclet>
                        <docletArtifact>
                            <groupId>capital.scalable</groupId>
                            <artifactId>spring-auto-restdocs-json-doclet</artifactId> 
                            <version>2.0.11</version>
                        </docletArtifact>
                        <destDir>generated-javadoc-json</destDir> 
                        <reportOutputDirectory>${project.build.directory}</reportOutputDirectory> 
                        <useStandardDocletOptions>false</useStandardDocletOptions>
                        <show>package</show>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
</build>

  • (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
  • (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json in build directory. Multiple directories can be listed by separating them with ,. The directories are processed in order and only the first found JSON file is used.
  • For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency.

Gradle

configurations {
    jsondoclet
}

ext {
    javadocJsonDir = file("$buildDir/generated-javadoc-json") 
}

dependencies {
    testCompile group: 'capital.scalable', name: 'spring-auto-restdocs-core', version: '2.0.11'
    jsondoclet group: 'capital.scalable', name: 'spring-auto-restdocs-json-doclet', version: '2.0.11' 
}

task jsonDoclet(type: Javadoc, dependsOn: compileJava) {
    source = sourceSets.main.allJava
    classpath = sourceSets.main.compileClasspath
    destinationDir = javadocJsonDir 
    options.docletpath = configurations.jsondoclet.files.asType(List)
    options.doclet = 'capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet'
    options.memberLevel = JavadocMemberLevel.PACKAGE
}

test {
    systemProperty 'org.springframework.restdocs.outputDir', snippetsDir 
    systemProperty 'org.springframework.restdocs.javadocJsonDir', javadocJsonDir 

    dependsOn jsonDoclet
}

jar {
    dependsOn asciidoctor
}

  • (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
  • (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json in build directory. Multiple directories can be listed by separating them with ,. The directories are processed in order and only the first found JSON file is used.
  • For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency.

  • Configure MockMvc or WebTestClient

MockMvc Test class

@Autowired
private WebApplicationContext context;

@Autowired
protected ObjectMapper objectMapper;

protected MockMvc mockMvc;

@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

@Before
public void setUp() throws Exception {
    this.mockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .addFilters(springSecurityFilterChain)
            .alwaysDo(JacksonResultHandlers.prepareJackson(objectMapper))
            .alwaysDo(MockMvcRestDocumentation.document("{class-name}/{method-name}",
                    Preprocessors.preprocessRequest(),
                    Preprocessors.preprocessResponse(
                            ResponseModifyingPreprocessors.replaceBinaryContent(),
                            ResponseModifyingPreprocessors.limitJsonArrayLength(objectMapper),
                            Preprocessors.prettyPrint())))
            .apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation)
                    .uris()
                    .withScheme("http")
                    .withHost("localhost")
                    .withPort(8080)
                    .and().snippets()
                    .withDefaults(CliDocumentation.curlRequest(),
                            HttpDocumentation.httpRequest(),
                            HttpDocumentation.httpResponse(),
                            AutoDocumentation.requestFields(),
                            AutoDocumentation.responseFields(),
                            AutoDocumentation.pathParameters(),
                            AutoDocumentation.requestParameters(),
                            AutoDocumentation.description(),
                            AutoDocumentation.methodAndPath(),
                            AutoDocumentation.section()))
            .build();
}

WebTestClient Test class

@Autowired
private ApplicationContext context;

protected WebTestClient webTestClient;

@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

@Before
public void setUp() throws Exception {
    this.webTestClient = WebTestClient
            .bindToApplicationContext(context)
            .apply(springSecurity())
            .configureClient()
            .baseUrl("http://localhost:8080/")
            .filter(documentationConfiguration(restDocumentation)
                .snippets()
                .withDefaults(WebTestClientInitializer.prepareSnippets(context),
                           CliDocumentation.curlRequest(),
                           HttpDocumentation.httpRequest(),
                           HttpDocumentation.httpResponse(),
                           AutoDocumentation.requestFields(),
                           AutoDocumentation.responseFields(),
                           AutoDocumentation.pathParameters(),
                           AutoDocumentation.requestParameters(),
                           AutoDocumentation.description(),
                           AutoDocumentation.methodAndPath(),
                           AutoDocumentation.section()))
            .build();
}

Snapshot build

If you want to experiment with snapshot builds, add this repository:

Maven

<repositories>
    <repository>
        <id>sonatype-snapshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

Gradle

repositories {
    mavenCentral()
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

Snippets

Method and path snippet

Method and path snippet takes a value (path) and method from RequestMapping annotation on the Controller’s method. The shorthand annotations GetMapping, PostMapping, etc. are supported as well.

Code

@GetMapping(value = "/items")
public ItemResponse getItems() { ... }

Result

GET /items

Description snippet

Description snippet outputs the Controller’s method’s Javadoc. Currently only basic br, p, ul and li tags are supported.

Code

/**
 * Returns list of all items.
 * <p>
 * Use query parameters to filter the list by:
 * <ul>
 *   <li>orderNumber</li>
 *   <li>type</li>
 * </ul>
 */
@GetMapping...
public ItemResponse getItems() { ... }

Result

Returns list of all items.

Use query parameters to filter the list by:
- orderNumber
- type

Authorization snippet

Authorization snippet does not help in adding authorization to your tests, but it makes it easy to document your authorization information. Usually, a custom RequestPostProcessor is used to add authorization and this is also the place where you add the documentation.

Code

protected RequestPostProcessor userToken() {
    return new RequestPostProcessor() {
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
            // <Code to get access token>
            request.addHeader("Authorization", "Bearer " + accessToken);
            return documentAuthorization(request, "User access token required.");
        }
    };
}

@Test
public void addItem() throws Exception {
    mockMvc.perform(post("/items").with(userToken())
    ...
}

Result

User access token required.

Path parameters snippet

Path parameters snippet automatically lists parameters in the path including their type, whether they are optional, and their Javadoc with constraints.

Code

/**
 * @param id ID of the item.
 */
@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }

Result

Parameter
Type
Optional
Description

id

String

false

ID of the item.

Configuration

Configuration available during MockMvc setup:

  • failOnUndocumentedParams - if build should fail on at least one undocumented parameter

Request parameters snippet

Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.

Code

/**
 * @param descMatch Lookup on description field.
 * @param lang Lookup on language.
 */
@GetMapping("search")
public Page<ItemResponse> searchItem(
 @RequestParam("desc") String descMatch,
 @RequestParam(value= "language", required = false, defaultValue = "en") String lang
 ) { ... }

Result

Parameter
Type
Optional
Description

desc

String

false

Lookup on description field.

language

String

true

Lookup on language.

Default value: "en".

Configuration

Configuration available during MockMvc setup:

  • failOnUndocumentedParams - if build should fail on at least one undocumented parameter

Model Attribute snippet

Model Attribute snippet automatically recognises POJOs annotated with @ModelAttribute annotation. Depending on HTTP method, it generates either section for request parameters (GET) or request fields (non GET). This snippet works also for model attributes without annotation, as specified in "any other argument" section of Spring MVC reference documentation.

Code

@GetMapping("search")
public SearchResult search(@ModelAttribute SearchCriteria criteria){ ... }

class SearchCriteria {
    /** Lookup on description field. */
    @NotBlank
    String descMatch;
    /** Lookup on language. */
    String lang;
}

Configuration

Configuration available during MockMvc setup:

  • failOnUndocumentedFields - if build should fail on at least one undocumented field

Request headers snippet

Request headers snippet automatically lists headers with their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.

Code

/**
 * @param lang Language we want the response in.
 */
@GetMapping("/all")
public ItemResponse getItem(
@RequestHeader(value = "Accept-Language", required = false, defaultValue = "en") String lang
) { ... }

Result

Parameter
Type
Optional
Description

Accept-Language

String

true

Language we want the response in.

Default value: "en".

Request fields snippet

Request fields snippet automatically lists all fields of the request class, determined from @RequestBody parameter of Controller’s method. List includes name, type, whether the field is optional, and its Javadoc with constraints.

Code

@PostMapping
public void addItem(@RequestBody ItemUpdateRequest request) { ... }

static class ItemUpdateRequest {
    /**
     * Item ID.
     */
    @NotBlank
    private Integer id;

    /**
     * Item description.
     */
    @Size(max = 20)
    private String description;
}

Result

Path
Type
Optional
Description

description

String

true

Item description.

Size must be between 0 and 20 inclusive.

id

Integer

false

Item ID.

Configuration

Configuration available during MockMvc setup:

  • failOnUndocumentedFields - if build should fail on at least one undocumented field
  • requestBodyAsType - specified class should be considered as request type instead of endpoint method’s parameter

Response fields snippet

Response fields snippet automatically lists all fields of the response class, determined from return value of Controller’s method. List includes name, type, whether the field is optional, and its Javadoc with constraints.

Code

@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }

static class ItemResponse {
    /**
     * Unique item ID.
     */
    @NotBlank
    private String id;

    /**
     * Item's order number.
     */
    @Min(1)
    private Integer orderNumber;
}

Result

Path
Type
Optional
Description

id

Integer

false

Unique item ID.

orderNumber

Integer

true

Item’s order number.

Must be at least 1.

Configuration

Configuration available during MockMvc setup:

  • failOnUndocumentedFields - if build should fail on at least one undocumented field
  • responseBodyAsType - specified class should be considered as response type instead of endpoint method’s return type

The section snippet combines most common snippets into one convenient file. It helps you being even more lazy, because a single line of AsciiDoc is sufficient to document one endpoint. Assuming of course that you already documented your code with Javadoc.

Asciidoc

include::{snippets}/your-endpoint/auto-section.adoc[]

Template itself

[[resources-{{link}}]]
=== {{title}}

include::auto-method-path.adoc[]

include::auto-description.adoc[]
{{#sections}}

==== {{header}}

{{#filenames}}
include::{{.}}.adoc[]
{{/filenames}}
{{/sections}}

Template placeholders

Placeholder
Example
Description

{fileName}

authorization

Snippet name hard-coded inside Snippet class.

{header}

Authorization

Snippet header hard-coded inside Snippet class.

{link}

items-id

Derived from the documentation string. For example: items/id.

{sections}

<iterable>

Contains all sections (Snippet classes) which are configured to be included in the section snippet. Moustache templater iterates over this and expands into separate header and link lines.

{snippets}

target/generated-snippets

Base snippet folder configured via org.springframework.restdocs.outputDir system property.

Timeline

Further Resources

Title
Author
Link
Type
Date

News

Search on Google
Search on Bing
Golden logo
By using this site, you agree to our Terms & Conditions.