tags | projects | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
This guide walks you through the process of building RESTful services using Spring. This is a bookmarks service example that adopts Building REST services with Spring, applies some refactoring and builds on it by writing tests to verify HATEOAS responses. Example takes advantage of the Spring REST Dcos to document the REST APIs for the service.
Example models a RESTful bookmark service. It simply collects a URI, and a description where all bookmarks belong to a user account. This relationship is modeled using JPA and Spring Data JPA repositories in the model module. Our application will use Spring Boot. A Spring Boot application is, at a minimum, a public static void main entry-point and the @EnableAutoConfiguration annotation. This tells Spring Boot to help out, wherever possible.
Example is using Spring REST Docs to document the RESTful service. Spring REST Docs uses Asciidoctor, a high quality hand-written documentation tool, and applies test driven approach to help guarantee the accuracy of service’s documentation. Spring REST Docs makes use of snippets produced by tests written with Spring MVC Test.
-
Apply the Asciidoctor plugin
plugins { id "org.asciidoctor.convert" version "1.5.2" }
-
Add a dependency on spring-restdocs-mockmvc in the testCompile configuration
dependencies { testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc:1.0.0.RELEASE' }
-
Configure a property to define the output location for generated snippets
ext { snippetsDir = file('build/generated-snippets') }
-
Configure the test task to add the snippets directory as an output
test { outputs.dir snippetsDir }
-
Configure the asciidoctor task
-
Define the source adoc templates directory for Asciidoctor to generate the html files from
-
Define an attribute named snippets that can be used when including the generated snippets in your documentation
-
Configure the snippets directory as an input
-
Make the task depend on the test task so that the tests are run before the documentation is created
-
asciidoctor {
sourceDir 'src/main/asciidoc'
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
}
And that is it. Really. All the tests written for the service in this test class will produce three snippets by default:
-
build/generated-snippets/curl-request.adoc
-
build/generated-snippets/http-request.adoc
-
build/generated-snippets/http-response.adoc
To explore all the more RestDocumentation and RestDocumentationResultHandler has to offer, please visit reference documentation for Spring REST Docs project.
Depending on documentation deployment strategy, documentation can be packaged inside the jar/war distribution or copied to e.g. a centralised documentation server on the intranet. To package it into the jar’s static directory simply make the Gradle 'jar' task depend on Gradle 'asciidoctor' task and specify the location in the package where the documentation is preferred to be copied to. When the project is built Asciidoctor originally generates the documentation in the build folder under src/main/asciidoc.
jar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5")
{
into 'static/docs'
}
}
As described earlier Spring REST Docs uses Spring’s MVC Test framework to make requests to the service that you are documenting. It then produces documentation snippets for the result’s request and response in specified location (for the adoc templates that live under /src/main/asciidoc to utilize). Spring MVC test needs to be setup in the following manner:
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");
private RestDocumentationResultHandler document;
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.document = document(
"{method-name}",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(this.document)
.build();
}
Although it is possible to package this service as a traditional WAR file for deployment to an external application server, the simpler approach demonstrated below creates a standalone application. You package everything in a single, executable JAR file, driven by a good old Java main() method. Along the way, you use Spring’s support for embedding the Tomcat servlet container as the HTTP runtime, instead of deploying to an external instance.
src/main/java/com/df/gs/rest/RestServicesTemplateApplication.java
link:src/main/java/com/df/gs/rest/RestServicesTemplateApplication.java[role=include]
@SpringBootApplication is a convenience annotation that adds all of the following:
-
@Configuration tags the class as a source of bean definitions for the application context.
-
@EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
-
Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
-
@ComponentScan tells Spring to look for other components, configurations, and services in the com.df.gs.rest package.
The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Did you notice that there wasn’t a single line of XML? No web.xml file either. This web application is 100% pure Java and you didn’t have to deal with configuring any plumbing or infrastructure.
Logging output is displayed. The service should be up and running within a few seconds.
This example is setup to have two profiles:
-
default profile uses embedded HSQLDB and logs at DEBUG level for the service
-
dev profile connects to a local installation of PostgreSQL and logging level is set to INFO
Notice the difference between database and logging levels in application.properties and application-dev.properties.
Now that the service is up, visit http://localhost:8080/mohammadali/bookmarks, and you should see the following response:
{
"_embedded": {
"bookmarkResources": [
{
"bookmark": {
"uri": "http://www.google.com",
"description": "Google search"
},
"_links": {
"bookmarks": {
"href": "http://localhost:8080/mohammadali/bookmarks"
},
"self": {
"href": "http://localhost:8080/mohammadali/bookmarks/1"
}
}
},
{
"bookmark": {
"uri": "http://www.youtube.com",
"description": "YouTube"
},
"_links": {
"bookmarks": {
"href": "http://localhost:8080/mohammadali/bookmarks"
},
"self": {
"href": "http://localhost:8080/mohammadali/bookmarks/2"
}
}
}
]
}
}
If you use Postman for testing web services, please find Postman collection and environment it depends on under src/main/test/resources/postman. All you need to do is to import them in to Postman and that’s it, they are ready to test the service.
Notice that the service has a dependency on Spring Boot Actuator. Spring Boot detects the dependency and magically adds production-ready features to help you monitor and manage your application. You can also try & test these e.g. to see the status of the application try http://<host>/health. For more visit its reference documentation.