diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index 0018d7d0..908a5fd8 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -174,27 +174,37 @@ include::{examples-dir}/com/example/Payload.java[tags=explicit-type] -[[documenting-your-api-query-parameters]] -=== Query parameters +[[documenting-your-api-request-parameters]] +=== Request parameters -A request's query parameters can be documented using `queryParameters` +A request's parameters can be documented using `requestParameters`. Request parameters +can be included in a `GET` requests query string: [source,java,indent=0] ---- -include::{examples-dir}/com/example/QueryParameters.java[tags=query-parameters] +include::{examples-dir}/com/example/RequestParameters.java[tags=request-parameters-query-string] ---- -<1> Produce a snippet describing the request's query parameters. Uses the static -`queryParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<2> Document a parameter named `page`. Uses the static `parameterWithName` method on +<1> Perform a `GET` request with two parameters in the query string. +<2> Produce a snippet describing the request's parameters. Uses the static +`requestParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. +<3> Document a parameter named `page`. Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document a parameter named `per_page`. +<4> Document a parameter named `per_page`. -The result is a snippet named `query-parameters.adoc` that contains a table describing -the query parameters that are supported by the resource. +They can also be included as form data in the body of a POST request: -When documenting query parameters, the test will fail if an undocumented query parameter -is used in the request. Similarly, the test will also fail if a documented query parameter -is not found in the request. +[source,java,indent=0] +---- +include::{examples-dir}/com/example/RequestParameters.java[tags=request-parameters-form-data] +---- +<1> Perform a `POST` request with a single parameter. + +In both cases, the result is a snippet named `request-parameters.adoc` that contains a +table describing the parameters that are supported by the resource. + +When documenting request parameters, the test will fail if an undocumented request +parameter is used in the request. Similarly, the test will also fail if a documented +request parameter is not found in the request. diff --git a/docs/src/test/java/com/example/QueryParameters.java b/docs/src/test/java/com/example/RequestParameters.java similarity index 61% rename from docs/src/test/java/com/example/QueryParameters.java rename to docs/src/test/java/com/example/RequestParameters.java index 4afcc35d..62e88291 100644 --- a/docs/src/test/java/com/example/QueryParameters.java +++ b/docs/src/test/java/com/example/RequestParameters.java @@ -18,25 +18,36 @@ package com.example; import static org.springframework.restdocs.RestDocumentation.document; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.restdocs.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.RestDocumentationRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.MockMvc; -public class QueryParameters { +public class RequestParameters { private MockMvc mockMvc; - public void queryParametersSnippet() throws Exception { - // tag::query-parameters[] - this.mockMvc.perform(get("/users?page=2&per_page=100")) + public void getQueryStringSnippet() throws Exception { + // tag::request-parameters-query-string[] + this.mockMvc.perform(get("/users?page=2&per_page=100")) // <1> .andExpect(status().isOk()) - .andDo(document("users", queryParameters( // <1> - parameterWithName("page").description("The page to retrieve"), // <2> - parameterWithName("per_page").description("Entries per page") // <3> + .andDo(document("users", requestParameters( // <2> + parameterWithName("page").description("The page to retrieve"), // <3> + parameterWithName("per_page").description("Entries per page") // <4> ))); - // end::query-parameters[] + // end::request-parameters-query-string[] + } + + public void postFormDataSnippet() throws Exception { + // tag::request-parameters-form-data[] + this.mockMvc.perform(post("/users").param("username", "Tester")) // <1> + .andExpect(status().isCreated()) + .andDo(document("create-user", requestParameters( + parameterWithName("username").description("The user's username") + ))); + // end::request-parameters-form-data[] } } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java index 3eb82ba2..8edaf3cc 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java @@ -22,7 +22,7 @@ import java.util.Map; import org.springframework.restdocs.snippet.AbstractDescriptor; /** - * A descriptor of a parameter in a query string + * A descriptor of a request or path parameter * * @author Andy Wilkinson * @see RequestDocumentation#parameterWithName diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java index fea071e3..9f3887f2 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java @@ -19,7 +19,11 @@ package org.springframework.restdocs.request; import java.util.Arrays; import java.util.Map; +import javax.servlet.ServletRequest; + import org.springframework.restdocs.snippet.Snippet; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; /** * Static factory methods for documenting aspects of a request sent to a RESTful API. @@ -33,8 +37,8 @@ public abstract class RequestDocumentation { } /** - * Creates a {@link ParameterDescriptor} that describes a query string parameter with - * the given {@code name}. + * Creates a {@link ParameterDescriptor} that describes a request or path parameter + * with the given {@code name}. * * @param name The name of the parameter * @return a {@link ParameterDescriptor} ready for further configuration @@ -44,57 +48,59 @@ public abstract class RequestDocumentation { } /** - * Returns a handler that will produce a snippet documenting the path parameters from - * the API call's request. + * Returns a snippet that will document the path parameters from the API call's + * request. * * @param descriptors The descriptions of the parameters in the request's path - * @return the handler + * @return the snippet + * @see PathVariable */ public static Snippet pathParameters(ParameterDescriptor... descriptors) { return new PathParametersSnippet(Arrays.asList(descriptors)); } /** - * Returns a handler that will produce a snippet documenting the path parameters from - * the API call's request. The given {@code attributes} will be available during - * snippet generation. + * Returns a snippet that will document the path parameters from the API call's + * request. The given {@code attributes} will be available during snippet rendering. * * @param attributes Attributes made available during rendering of the path parameters * snippet * @param descriptors The descriptions of the parameters in the request's path - * @return the handler + * @return the snippet + * @see PathVariable */ public static Snippet pathParameters(Map attributes, ParameterDescriptor... descriptors) { - return new PathParametersSnippet(attributes, - Arrays.asList(descriptors)); + return new PathParametersSnippet(attributes, Arrays.asList(descriptors)); } /** - * Returns a handler that will produce a snippet documenting the query parameters from - * the API call's request. + * Returns a snippet that will document the request parameters from the API call's + * request. * - * @param descriptors The descriptions of the request's query parameters - * @return the handler + * @param descriptors The descriptions of the request's parameters + * @return the snippet + * @see RequestParam + * @see ServletRequest#getParameterMap() */ - public static Snippet queryParameters(ParameterDescriptor... descriptors) { - return new QueryParametersSnippet(Arrays.asList(descriptors)); + public static Snippet requestParameters(ParameterDescriptor... descriptors) { + return new RequestParametersSnippet(Arrays.asList(descriptors)); } /** - * Returns a handler that will produce a snippet documenting the query parameters from - * the API call's request. The given {@code attributes} will be available during - * snippet generation. + * Returns a snippet that will document the request parameters from the API call's + * request. The given {@code attributes} will be available during snippet rendering. * - * @param attributes Attributes made available during rendering of the query + * @param attributes Attributes made available during rendering of the request * parameters snippet - * @param descriptors The descriptions of the request's query parameters - * @return the handler + * @param descriptors The descriptions of the request's parameters + * @return the snippet + * @see RequestParam + * @see ServletRequest#getParameterMap() */ - public static Snippet queryParameters(Map attributes, + public static Snippet requestParameters(Map attributes, ParameterDescriptor... descriptors) { - return new QueryParametersSnippet(attributes, - Arrays.asList(descriptors)); + return new RequestParametersSnippet(attributes, Arrays.asList(descriptors)); } } diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java similarity index 66% rename from spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java index 0bd11b88..1916c9b6 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestParametersSnippet.java @@ -20,24 +20,32 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.servlet.ServletRequest; + import org.springframework.restdocs.snippet.Snippet; import org.springframework.restdocs.snippet.SnippetException; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.web.bind.annotation.RequestParam; /** - * A {@link Snippet} that documents the query parameters supported by a RESTful resource. + * A {@link Snippet} that documents the request parameters supported by a RESTful + * resource. + *

+ * Request parameters are sent as part of the query string or as posted from data. * * @author Andy Wilkinson + * @see ServletRequest#getParameterMap() + * @see RequestParam */ -class QueryParametersSnippet extends AbstractParametersSnippet { +class RequestParametersSnippet extends AbstractParametersSnippet { - QueryParametersSnippet(List descriptors) { + RequestParametersSnippet(List descriptors) { this(null, descriptors); } - QueryParametersSnippet(Map attributes, + RequestParametersSnippet(Map attributes, List descriptors) { - super("query-parameters", attributes, descriptors); + super("request-parameters", attributes, descriptors); } @Override @@ -45,14 +53,14 @@ class QueryParametersSnippet extends AbstractParametersSnippet { Set missingParameters) { String message = ""; if (!undocumentedParameters.isEmpty()) { - message += "Query parameters with the following names were not documented: " + message += "Request parameters with the following names were not documented: " + undocumentedParameters; } if (!missingParameters.isEmpty()) { if (message.length() > 0) { message += ". "; } - message += "Query parameters with the following names were not found in the request: " + message += "Request parameters with the following names were not found in the request: " + missingParameters; } throw new SnippetException(message); diff --git a/spring-restdocs/src/main/resources/org/springframework/restdocs/templates/default-query-parameters.snippet b/spring-restdocs/src/main/resources/org/springframework/restdocs/templates/default-request-parameters.snippet similarity index 100% rename from spring-restdocs/src/main/resources/org/springframework/restdocs/templates/default-query-parameters.snippet rename to spring-restdocs/src/main/resources/org/springframework/restdocs/templates/default-request-parameters.snippet diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java index f3642d76..7f8cb279 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/RestDocumentationIntegrationTests.java @@ -31,7 +31,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.requestF import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.restdocs.response.ResponsePostProcessors.maskLinks; import static org.springframework.restdocs.response.ResponsePostProcessors.prettyPrintContent; import static org.springframework.restdocs.response.ResponsePostProcessors.removeHeaders; @@ -148,18 +148,18 @@ public class RestDocumentationIntegrationTests { } @Test - public void queryParametersSnippet() throws Exception { + public void requestParametersSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(new RestDocumentationConfigurer()).build(); mockMvc.perform(get("/").param("foo", "bar").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andDo(document("links", queryParameters(parameterWithName("foo") + .andDo(document("links", requestParameters(parameterWithName("foo") .description("The description")))); assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", "http-response.adoc", "curl-request.adoc", - "query-parameters.adoc"); + "request-parameters.adoc"); } @Test diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java similarity index 57% rename from spring-restdocs/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java rename to spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java index fe0efd90..fd74544d 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestParametersSnippetTests.java @@ -44,11 +44,11 @@ import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; import org.springframework.restdocs.test.ExpectedSnippet; /** - * Tests for {@link QueryParametersSnippet} + * Tests for {@link RequestParametersSnippet} * * @author Andy Wilkinson */ -public class QueryParametersSnippetTests { +public class RequestParametersSnippetTests { @Rule public ExpectedException thrown = ExpectedException.none(); @@ -57,108 +57,106 @@ public class QueryParametersSnippetTests { public ExpectedSnippet snippet = new ExpectedSnippet(); @Test - public void undocumentedQueryParameter() throws IOException { + public void undocumentedParameter() throws IOException { this.thrown.expect(SnippetException.class); this.thrown - .expectMessage(equalTo("Query parameters with the following names were" + .expectMessage(equalTo("Request parameters with the following names were" + " not documented: [a]")); - new QueryParametersSnippet(Collections. emptyList()) - .document("undocumented-query-parameter", - result(get("/").param("a", "alpha"))); + new RequestParametersSnippet(Collections. emptyList()) + .document("undocumented-parameter", result(get("/").param("a", "alpha"))); } @Test - public void missingQueryParameter() throws IOException { + public void missingParameter() throws IOException { this.thrown.expect(SnippetException.class); this.thrown - .expectMessage(equalTo("Query parameters with the following names were" + .expectMessage(equalTo("Request parameters with the following names were" + " not found in the request: [a]")); - new QueryParametersSnippet(Arrays.asList(parameterWithName("a") - .description("one"))).document("missing-query-parameter", - result(get("/"))); + new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( + "one"))).document("missing-parameter", result(get("/"))); } @Test - public void undocumentedAndMissingQueryParameters() throws IOException { + public void undocumentedAndMissingParameters() throws IOException { this.thrown.expect(SnippetException.class); this.thrown - .expectMessage(equalTo("Query parameters with the following names were" - + " not documented: [b]. Query parameters with the following" + .expectMessage(equalTo("Request parameters with the following names were" + + " not documented: [b]. Request parameters with the following" + " names were not found in the request: [a]")); - new QueryParametersSnippet(Arrays.asList(parameterWithName("a") - .description("one"))).document( - "undocumented-and-missing-query-parameters", - result(get("/").param("b", "bravo"))); + new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description( + "one"))).document("undocumented-and-missing-parameters", result(get("/") + .param("b", "bravo"))); } @Test - public void queryParameterSnippetFromRequestParameters() throws IOException { - this.snippet.expectQueryParameters("query-parameter-snippet-request-parameters") - .withContents( - tableWithHeader("Parameter", "Description").row("a", "one").row( - "b", "two")); - new QueryParametersSnippet(Arrays.asList(parameterWithName("a") - .description("one"), parameterWithName("b").description("two"))) - .document("query-parameter-snippet-request-parameters", result(get("/") - .param("a", "bravo").param("b", "bravo"))); - } - - @Test - public void queryParameterSnippetFromRequestUriQueryString() throws IOException { - this.snippet.expectQueryParameters( - "query-parameter-snippet-request-uri-query-string").withContents( + public void requestParameterSnippetFromRequestParameters() throws IOException { + this.snippet.expectRequestParameters( + "request-parameter-snippet-request-parameters").withContents( tableWithHeader("Parameter", "Description").row("a", "one").row("b", "two")); - new QueryParametersSnippet(Arrays.asList(parameterWithName("a") - .description("one"), parameterWithName("b").description("two"))) - .document( - "query-parameter-snippet-request-uri-query-string", - result(get("/?a=alpha&b=bravo").requestAttr( - RestDocumentationContext.class.getName(), - new RestDocumentationContext(null)))); + new RequestParametersSnippet(Arrays.asList( + parameterWithName("a").description("one"), parameterWithName("b") + .description("two"))).document( + "request-parameter-snippet-request-parameters", + result(get("/").param("a", "bravo").param("b", "bravo"))); } @Test - public void queryParametersWithCustomDescriptorAttributes() throws IOException { - this.snippet.expectQueryParameters( - "query-parameters-with-custom-descriptor-attributes").withContents( + public void requestParameterSnippetFromRequestUriQueryString() throws IOException { + this.snippet.expectRequestParameters( + "request-parameter-snippet-request-uri-query-string").withContents( + tableWithHeader("Parameter", "Description").row("a", "one").row("b", + "two")); + new RequestParametersSnippet(Arrays.asList( + parameterWithName("a").description("one"), parameterWithName("b") + .description("two"))).document( + "request-parameter-snippet-request-uri-query-string", + result(get("/?a=alpha&b=bravo").requestAttr( + RestDocumentationContext.class.getName(), + new RestDocumentationContext(null)))); + } + + @Test + public void requestParametersWithCustomDescriptorAttributes() throws IOException { + this.snippet.expectRequestParameters( + "request-parameters-with-custom-descriptor-attributes").withContents( tableWithHeader("Parameter", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - when(resolver.resolveTemplateResource("query-parameters")).thenReturn( - snippetResource("query-parameters-with-extra-column")); + when(resolver.resolveTemplateResource("request-parameters")).thenReturn( + snippetResource("request-parameters-with-extra-column")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine( resolver)); request.addParameter("a", "bravo"); request.addParameter("b", "bravo"); - new QueryParametersSnippet(Arrays.asList( + new RequestParametersSnippet(Arrays.asList( parameterWithName("a").description("one").attributes( key("foo").value("alpha")), parameterWithName("b").description("two").attributes( key("foo").value("bravo")))).document( - "query-parameters-with-custom-descriptor-attributes", result(request)); + "request-parameters-with-custom-descriptor-attributes", result(request)); } @Test - public void queryParametersWithCustomAttributes() throws IOException { - this.snippet.expectQueryParameters("query-parameters-with-custom-attributes") + public void requestParametersWithCustomAttributes() throws IOException { + this.snippet.expectRequestParameters("request-parameters-with-custom-attributes") .withContents(startsWith(".The title")); TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - when(resolver.resolveTemplateResource("query-parameters")).thenReturn( - snippetResource("query-parameters-with-title")); + when(resolver.resolveTemplateResource("request-parameters")).thenReturn( + snippetResource("request-parameters-with-title")); MockHttpServletRequest request = new MockHttpServletRequest(); request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine( resolver)); request.addParameter("a", "bravo"); request.addParameter("b", "bravo"); - new QueryParametersSnippet( + new RequestParametersSnippet( attributes(key("title").value("The title")), Arrays.asList( parameterWithName("a").description("one").attributes( key("foo").value("alpha")), parameterWithName("b") .description("two").attributes(key("foo").value("bravo")))) - .document("query-parameters-with-custom-attributes", result(request)); + .document("request-parameters-with-custom-attributes", result(request)); } private FileSystemResource snippetResource(String name) { diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java b/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java index c1e95814..c3b72eb3 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java @@ -30,8 +30,8 @@ import org.springframework.restdocs.snippet.TemplatedSnippet; import org.springframework.restdocs.test.SnippetMatchers.SnippetMatcher; /** - * The {@code ExpectedSnippet} rule is used to verify that a - * {@link TemplatedSnippet} has generated the expected snippet. + * The {@code ExpectedSnippet} rule is used to verify that a {@link TemplatedSnippet} has + * generated the expected snippet. * * @author Andy Wilkinson */ @@ -130,8 +130,8 @@ public class ExpectedSnippet implements TestRule { return this; } - public ExpectedSnippet expectQueryParameters(String name) { - expect(name, "query-parameters"); + public ExpectedSnippet expectRequestParameters(String name) { + expect(name, "request-parameters"); return this; } diff --git a/spring-restdocs/src/test/resources/custom-snippet-templates/query-parameters-with-extra-column.snippet b/spring-restdocs/src/test/resources/custom-snippet-templates/request-parameters-with-extra-column.snippet similarity index 100% rename from spring-restdocs/src/test/resources/custom-snippet-templates/query-parameters-with-extra-column.snippet rename to spring-restdocs/src/test/resources/custom-snippet-templates/request-parameters-with-extra-column.snippet diff --git a/spring-restdocs/src/test/resources/custom-snippet-templates/query-parameters-with-title.snippet b/spring-restdocs/src/test/resources/custom-snippet-templates/request-parameters-with-title.snippet similarity index 100% rename from spring-restdocs/src/test/resources/custom-snippet-templates/query-parameters-with-title.snippet rename to spring-restdocs/src/test/resources/custom-snippet-templates/request-parameters-with-title.snippet