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

  1. Does it work with Spring Web MVC tests?

    Yes, take a look at the example.

  2. Does it work with Spring WebFlux?

    Yes, take a look at the example.

  3. 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. Take a look at the example.

  4. 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.

  5. Does it work with REST Assured tests?

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

  6. Is Jackson required for automatic field documentation?

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

  7. 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).

  8. 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.

  9. 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.

  10. Is HATEOAS supported?

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

  11. 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

  1. Setup project for Spring REST Docs

  2. 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 (1)
                        </org.springframework.restdocs.outputDir>
                        <org.springframework.restdocs.javadocJsonDir>
                            ${project.build.directory}/generated-javadoc-json (2)
                        </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> (3)
                                <version>2.0.11</version>
                            </docletArtifact>
                            <destDir>generated-javadoc-json</destDir> (2)
                            <reportOutputDirectory>${project.build.directory}</reportOutputDirectory> (2)
                            <useStandardDocletOptions>false</useStandardDocletOptions>
                            <show>package</show>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
    1 (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
    2 (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.
    3 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") (2)
    }
    
    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' (3)
    }
    
    task jsonDoclet(type: Javadoc, dependsOn: compileJava) {
        source = sourceSets.main.allJava
        classpath = sourceSets.main.compileClasspath
        destinationDir = javadocJsonDir (2)
        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 (1)
        systemProperty 'org.springframework.restdocs.javadocJsonDir', javadocJsonDir (2)
    
        dependsOn jsonDoclet
    }
    
    jar {
        dependsOn asciidoctor
    }
    1 (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
    2 (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.
    3 For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency.
  3. 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"
    }
}

Sample applications

This projects includes a Java Web MVC example, a Java WebFlux example and a Kotlin Web MVC example application that demonstrate most features.

The generated documentation can be viewed for the Java Web MVC example, the Java WebFlux example and the Kotlin Web MCV example.

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

id

Integer

false

Item ID.

description

String

true

Item description.

Size must be between 0 and 20 inclusive.

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

Section snippet

Section snippets only work if AsciiDoc is used. Markdown is not supported, because Markdown has no includes.

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

{link}

items-id

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

{title}

Get Item By Id

See Section title.

{snippets}

target/generated-snippets

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

{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.

{header}

Authorization

Snippet header hard-coded inside Snippet class.

{fileName}

authorization

Snippet name hard-coded inside Snippet class.

Snippet configuration

By default, these snippets in the following order are linked inside section snippet:

Snippet name

Example

Description

method-path

POST /items/{id}

Method and path snippet is used to determine the HTTP method and path (non-configurable).

description

Get item by ID.

Description snippet reads the Javadoc on the method (non-configurable).

authorization

Resource is public.

Authorization snippet can be used for custom authorization information, e.g. the OAuth role required for the endpoint

path-parameters

<table>

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

request-parameters

<table>

Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc.

request-fields

<table>

Request fields snippet automatically lists all fields of the request body including their type, whether they are optional, and their Javadoc.

response-fields

<table>

Response fields snippet automatically lists all fields of the response body including their type, whether they are optional, and their Javadoc.

curl-request

$ curl 'http://localhost/items/1'

The original cURL request snippet from Spring REST Docs is used here to provide a cURL example of the request.

http-response

HTTP/1.1 200 OK
{ id: "myId", …​ }

The original HTTP response snippet from Spring REST Docs is used here to provide the HTTP response from the test.

Customisation options

Sections and order

If you want to configure custom sections and their order, instead of using AutoDocumentation.section() you can use AutoDocumentation.sectionBuilder() in the following manner:

Configuring section snippet
AutoDocumentation.sectionBuilder()
    .snippetNames( (1)
        SnippetRegistry.PATH_PARAMETERS,
        SnippetRegistry.REQUEST_PARAMETERS,
        SnippetRegistry.REQUEST_FIELDS,
        SnippetRegistry.RESPONSE_FIELDS)
    .skipEmpty(true) (2)
    .build()
1 only selected snippets will be used in the specified order
2 whether snippets having no content should be skipped from section
Section title

Section title uses text from @title Javadoc tag on the handler method. If missing, it’s derived from the method name, e.g. getItemByIdGet Item By Id.

If you use @title tag and want to generate regular Javadoc as well, you need to include this tag among custom tags in plugin configuration.

Custom maven-javadoc-plugin configuration
<plugin>
    <artifactId>maven-javadoc-plugin</artifactId>
    <executions>
        <execution>
            <id>attach-javadocs</id>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <tags>
                    <tag>
                        <name>title</name>
                        <placement>m</placement>
                    </tag>
                </tags>
            </configuration>
        </execution>
    </executions>
</plugin>

Custom snippet in section

If you want your custom snippet to be part of section snippet with automatically recognised header and include link (file name), you have to implement SectionSupport interface. Then, inside MockMvc configuration, add this snippet to the list of snippets using documentation configuration’s builder withDefaults(…​) and include snippet name among SectionBuilder’s snippetNames(…​).

Links snippet lists all possible links inside the response. In most cases, links cannot be determined from the response class, because they are highly dynamic depending on a resource state and often just stored in a Map. Therefore this snippet needs a custom class describing possible links, with field names acting as link _rel_s and field’s Javadoc and annotions used as usually to determine the comment, optionality, deprecation, etc. List includes name, whether the link is optional, and its Javadoc description.

Code
@GetMapping
public ItemResponse getItem() {
    ItemResponse response = new ItemResponse();
    response.add(linkTo(methodOn(ItemResource.class).getItem()).withSelfRel());
    return response;
}

static class ItemResponse extends org.springframework.hateoas.ResourceSupport {
  ...
}

The corresponding test method needs to be configured with documentation type:

MockMvc test method
@Test
public void getItem() throws Exception {
    mockMvc.perform(get(...))
    ...
        .andDo(links().documentationType(LinksDocumentation.class));
}

static class LinksDocumentation {
    /**
     * Link to this item.
     */
    @NotNull
    private String self;

    /**
     * Link to item's metadata.
     */
    private String metadata;
}
Result
Path Optional Description

self

false

Link to this item.

metadata

true

Link to item’s metadata.

Configuration

Configuration available during MockMvc setup:

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

  • documentationType - specified class should be considered for documentation

Embedded snippet

Embedded snippet lists all embedded resources of the response. Similarly to LinksSnippet, documentation is determined from custom documentation class. List includes name, type, whether the field is optional, and its Javadoc with constraints.

Code
@GetMapping
public ItemResponse getItem() {
    ItemResponse response = new ItemResponse();
    response.embed(getAttributes());
    response.embed(getMetadata());
    return response;
}

static class ItemResponse extends EmbeddedSupport {
  ...
}

The corresponding test method needs to be configured with documentation type:

MockMvc test method
@Test
public void getItem() throws Exception {
    mockMvc.perform(get(...))
    ...
        .andDo(embedded().documentationType(EmbeddedDocumentation.class));
}

static class EmbeddedDocumentation {
    /**
     * Item's attributes.
     */
    @NotNull
    private Attributes attributes;

    /**
     * Item's metadata.
     */
    private List<Metadata> metadata;
}
Result
Path Type Optional Description

attributes

Object

false

Item’s attributes.

metadata

Array[Object]

true

Item’s metadata

Configuration

Configuration available during MockMvc setup:

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

  • documentationType - specified class should be considered for documentation

Section snippet

Section snippets only work if AsciiDoc is used. Markdown is not supported, because Markdown has no includes.

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

{link}

items-id

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

{title}

Get Item By Id

See Section title.

{snippets}

target/generated-snippets

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

{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.

{header}

Authorization

Snippet header hard-coded inside Snippet class.

{fileName}

authorization

Snippet name hard-coded inside Snippet class.

Snippet configuration

By default, these snippets in the following order are linked inside section snippet:

Snippet name

Example

Description

method-path

POST /items/{id}

Method and path snippet is used to determine the HTTP method and path (non-configurable).

description

Get item by ID.

Description snippet reads the Javadoc on the method (non-configurable).

authorization

Resource is public.

Authorization snippet can be used for custom authorization information, e.g. the OAuth role required for the endpoint

path-parameters

<table>

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

request-parameters

<table>

Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc.

request-fields

<table>

Request fields snippet automatically lists all fields of the request body including their type, whether they are optional, and their Javadoc.

response-fields

<table>

Response fields snippet automatically lists all fields of the response body including their type, whether they are optional, and their Javadoc.

curl-request

$ curl 'http://localhost/items/1'

The original cURL request snippet from Spring REST Docs is used here to provide a cURL example of the request.

http-response

HTTP/1.1 200 OK
{ id: "myId", …​ }

The original HTTP response snippet from Spring REST Docs is used here to provide the HTTP response from the test.

Customisation options

Sections and order

If you want to configure custom sections and their order, instead of using AutoDocumentation.section() you can use AutoDocumentation.sectionBuilder() in the following manner:

Configuring section snippet
AutoDocumentation.sectionBuilder()
    .snippetNames( (1)
        SnippetRegistry.PATH_PARAMETERS,
        SnippetRegistry.REQUEST_PARAMETERS,
        SnippetRegistry.REQUEST_FIELDS,
        SnippetRegistry.RESPONSE_FIELDS)
    .skipEmpty(true) (2)
    .build()
1 only selected snippets will be used in the specified order
2 whether snippets having no content should be skipped from section
Section title

Section title uses text from @title Javadoc tag on the handler method. If missing, it’s derived from the method name, e.g. getItemByIdGet Item By Id.

If you use @title tag and want to generate regular Javadoc as well, you need to include this tag among custom tags in plugin configuration.

Custom maven-javadoc-plugin configuration
<plugin>
    <artifactId>maven-javadoc-plugin</artifactId>
    <executions>
        <execution>
            <id>attach-javadocs</id>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <tags>
                    <tag>
                        <name>title</name>
                        <placement>m</placement>
                    </tag>
                </tags>
            </configuration>
        </execution>
    </executions>
</plugin>

Custom snippet in section

If you want your custom snippet to be part of section snippet with automatically recognised header and include link (file name), you have to implement SectionSupport interface. Then, inside MockMvc configuration, add this snippet to the list of snippets using documentation configuration’s builder withDefaults(…​) and include snippet name among SectionBuilder’s snippetNames(…​).

Custom snippet

Custom snippets are created by subclassing the Snippet interface (or more conveniently TemplatedSnippet). Template for the snippet must be put into org/springframework/restdocs/templates/asciidoctor (optionally also org/springframework/restdocs/templates/markdown) resource package. The template name must correspond with the snippet name passed to the class constructor. You can also include your custom snippet in a section.

Snippet customization

Provide your own template in org/springframework/restdocs/templates/asciidoctor (or markdown) package of your resources. You can override REST Docs templates and Auto REST Docs templates. Important is to omit default- part from the template name so that the resolution algorithm will prefer this custom template. See also original documentation for additional details.

Documenting constraints

Spring REST Docs has support for documenting constraints, but using them requires a bit of work and you have to decide on how to include them. Here we are going a step further and automatically use constraints and the constraint groups to derive whether a field is optional and to add the constraint descriptions to the field descriptions.

Example
/**
 * Username
 */
@NotNull
@Size(min = 2, max = 50)
private String username;
Result
Parameter Type Optional Description

username

String

false

Username.

Size must be between 2 and 50.

Constraints that determine optionality

The following three constraints are used to determine whether a field is optional. Their descriptions are not included in the field descriptions.

  • NotNull

  • NotEmpty

  • NotBlank

Along with JSR-303 annotations, optionality is also determined based on:

  • required parameter of @RequestParam

  • field type being java.util.Optional

  • Koltin field being non-nullable

Custom constraints

Descriptions for custom constraints can be provided in org.springframework.restdocs.constraints.ConstraintDescriptions.properties. If this file can be loaded as a resource, descriptions are extracted. Each description is a property where the key is the full class name of the annotation suffixed with .description. The value is the description and can contain placeholders for annotation methods, e.g. ${value} to get the content of value(). For more details, see original documentation of here.

Example for the constraint annotation myproject.constraints.OneOf:

ConstraintDescriptions.properties
myproject.constraints.OneOf.description=Must be one of ${value}

Enums

Constraint message for enum is appended to description field as list of allowed values, e.g. Must be one of [A, B]. If you want to customize this message, use above mentioned ConstraintDescriptions.properties approach. The list of values is represented by the ${value} placeholder.

Readable constraint values

Spring REST Docs just calls toString() objects extracted from the constraint for documentation. Here we do a bit more on the following two types:

  • Array: The elements are extracted and toString() is called on each element. The resulting strings are wrapped in brackets and separated by commas, i.e. [element1.toString(), element2.toString(), …​].

  • Class: An instance with the default constructor is created and toString() is called on the instance.

Constraint groups

Constraint groups are also included in the documentation if a description is provided for them. The descriptions are provided in the same way as for custom constraints.

Examples

Descriptions for the examples
ConstraintDescriptions.properties
myproject.constraints.groups.German.description=DE: ${value}
myproject.constraints.groups.English.description=EN: ${value}
myproject.constraints.OneOf.description=Must be one of ${value}
Optionality description
Code
@NotNull(groups = English.class)
private String field;
Result
Parameter Type Optional Description

field

String

true
EN: false

Field description example
Code
@OneOf(value = {"big", "small"}, groups = English.class)
private String field;
Result
Parameter Type Optional Description

field

String

true

EN: Must be one of [big, small]

Other

Paging support

Paging support in an endpoint is automatically recognized if request handler method contains Pageable parameter and response is of type Page. This is then mentioned in request and response snippets by linking to common section explaining the details of supported paging parameters/fields. This section has to be created manually and must be tagged as [[overview-pagination]]. See example project for paging section template.

Preprocessors

Preprocessors are available for use in order to improve quality of endpoint examples.

For a list of standard preprocessors see Preprocessors.

Deprecation support

Endpoint or field will be marked as deprecated if at least one of these is present:

  • method / field is annotated with @Deprecated

  • method’s / field’s Javadoc has @deprecated tag

Deprecation is shown in the title of an endpoint (also visible in TOC) and description. Field’s deprecation is shown in description column of request/response table.
See how it looks in the example project.

See tag support

@see Javadoc tag comment will be added to the main description section of an endpoint or a field.
Currently supports only HTML links. Does not support Java code references.
See how it looks in the example project.

Localization support

For custom translations of section titles, table headers and other messages:

  1. Create a file SnippetMessages.properties in capital.scalable.restdocs.i18n package.

  2. Add translations to the file using keys from DefaultSnippetMessages.

Recursive structures support

To prevent infinite loop in recursive structures, annotate the recursive field with the @RestdocsNotExpanded annotation from the spring-auto-restdocs-annotations maven module.

Subtypes

There are two forms of subtypes supported, automatic via Jackson annotations and manual via MockMvc configuration. You can even combine them together if necessary.

Jackson subtypes

Standard JsonSubTypes annotation and it’s configuration is supported.

Jackson annotations
@JsonTypeInfo(use = NAME, include = PROPERTY, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Sub1.class, name = "1"),
        @JsonSubTypes.Type(value = Sub2.class, name = "2")
})
abstract class Parent {
    private String type;
    private String common;
}

class Sub1 extends Parent {
    private Boolean sub1;
}

class Sub2 extends Parent {
    private Integer sub2;
}

Custom subtypes

If for any reason Jackson annotations are not desired or possible, you can configure completely custom subtypes via MockMvc using standard JacksonResultHandlers.prepareJackson result handler with custom TypeMapping.

MockMvc configuration
.alwaysDo(prepareJackson(objectMapper, new TypeMapping()
        .mapSubtypes(Parent.class, Sub1.class, Sub2.class)))

Discriminator text

For better understanding which field is included when in the final documentation, ConstraintDescriptions.properties can be used to provide a message, which will be then applied to all fields coming from that particular subtype.

ConstraintDescriptions.properties
mypackage.Sub1.description=(available when type=2)

Integrations

WireMock

WireMock stubs can be generated with Spring REST Docs, see Generating Stubs using REST Docs. This also works with Spring Auto REST Docs. However, the corresponding snippet does not register automatically and thus has to be registered manually.

The following code shows how the new WireMockSnippet() can be registered.

WireMock snippet registration example
public void setUp() {
     this.mockMvc = MockMvcBuilders
                .webAppContextSetup(context)
                .alwaysDo(prepareJackson(objectMapper))
                .alwaysDo(document("{class-name}/{method-name}",
                                   preprocessRequest(), commonResponsePreprocessor()))
                .apply(documentationConfiguration(restDocumentation)
                               .uris()
                               .and().snippets()
                               .withDefaults(curlRequest(), httpRequest(), httpResponse(),
                                             requestFields(), responseFields(), pathParameters(),
                                             requestParameters(), description(), methodAndPath(),
                                             section(), new WireMockSnippet()))
                .build();
}

Contributing

Pull Requests

For code formatting please use intellij-codestyle.xml.

Building from source

Install Spring REST Docs test JAR

The Spring REST Docs test JAR is not published via Maven, but this project relies on it. If you want to build this project yourself, you need to install the test JAR. The test JAR is located in the lib folder and can be installed as follows:

Bash
mvn install:install-file -Dfile=lib/spring-restdocs-core-2.0.5.RELEASE-test.jar \
    -DgroupId=org.springframework.restdocs -DartifactId=spring-restdocs-core \
    -Dversion=2.0.5.RELEASE -Dpackaging=test-jar -Dclassifier=test

Install Dokka Core test JAR

Dokka publishes no test JAR, but the Spring Auto REST Docs Dokka extension uses their test utilities. If you want to build this project yourself, you need to install the test JAR. The test JAR is located in the lib folder and can be installed as follows:

Bash
mvn install:install-file -Dfile=lib/dokka-core-0.10.1-tests.jar \
     -DgroupId=org.jetbrains.dokka -DartifactId=dokka-core -Dversion=0.10.1 \
     -Dpackaging=jar -Dclassifier=test

Build Spring REST Docs test JAR

Building the Spring REST Docs test JAR is not required to build the project, but if you ever want to upgrade the version of Spring REST Docs in this project this step has to be done.

We use version 2.0.5.RELEASE of Spring REST Docs in this example.

You find the currently required version in pom.xml:

Maven
<dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-core</artifactId>
    <version>2.0.5.RELEASE</version>
    <classifier>test</classifier>
    <scope>test</scope>
</dependency>

Clone and build a specific version of Spring REST Docs:

Bash
git clone git@github.com:spring-projects/spring-restdocs.git
cd spring-restdocs
git fetch --tags
git checkout tags/v2.0.5.RELEASE
./gradlew build

Afterwards the test JAR is located at spring-restdocs/spring-restdocs-core/build/libs/spring-restdocs-core-2.0.5.RELEASE-test.jar and has to be installed with the Maven command shown in the section above.

Build Dokka Core test JAR

Building the Dokka core test JAR is not required to build the project, but if you ever want to upgrade the version of Dokka in this project this step has to be done.

We use version 0.10.1 of Dokka in this example.

Clone and checkout a specific version of Dokka:

Bash
git clone git@github.com:Kotlin/dokka.git
cd dokka
git checkout 0.10.1

Add the following snippet at the end of core/build.gradle:

task testJar(type: Jar) {
    classifier = 'tests'
    from sourceSets.test.output
}

To speed the build up and avoid test issues, once can reduce the list of sub-projects in settings.gradle to just core and integration.

Make sure to use JDK 8 for the build. Create the test JAR by running the following command on the top-level:

Bash
./gradlew testJar

Afterwards the test JAR is located at core/build/libs/core-0.10.1-SNAPSHOT-tests.jar

Build

Bash (in root folder)
mvn install