© 2008-2017 The original authors.
Note
|
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
Preface
Project metadata
-
Version control - http://github.com/paulcwarren/spring-content/
-
Bugtracker - http://github.com/paulcwarren/spring-content/issues
-
Release repository - https://repo1.maven.org/maven2/
-
Snapshots repository - https://oss.sonatype.org/content/repositories/snapshots
1. Introduction
REST web services have become the number one means for application integration on the web. In its core, REST defines that a system consists of resources that clients interact with. These resources are often also implemented in a hypermedia driven way. Spring MVC offers a solid foundation to build theses kinds of services. But implementing even the simplest REST web services for a multi-domain object system can be quite tedious and result in a lot of boilerplate code.
Spring Content REST builds on top of Spring Content stores and automatically exports those as REST resources. It leverages REST to expose end-points for each content resource and it also optionally integrates with Spring Data REST’s hypermedia API to allow clients to find content resources that have been associated with Spring Data entities.
Spring Content REST officially supports:
2. Getting Started
2.1. Introduction
Spring Content REST is itself a Spring MVC application and is designed in such a way that it should integrate with your existing Spring MVC applications with very little effort.
2.2. Adding Spring Content REST to a Spring Boot project
The simplest way to get to started is if you are building a Spring Boot application. That’s because Spring Content REST has both a starter as well as auto-configuration.
dependencies {
...
compile("com.github.paulcwarren:spring-content-rest-boot-starter:${version}")
...
}
<dependencies>
...
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>${version}</version>
</dependency>
...
</dependencies>
When using Spring Boot, Spring Content REST gets configured automatically.
2.3. Adding Spring Content REST to a Gradle Project
To add Spring Content REST to a Gradle-based project, add the spring-content-rest artifact to your compile-time dependencies:
dependencies {
...
compile("com.github.paulcwarren:spring-content-rest:${version}")
...
}
2.4. Adding Spring Content REST to a Maven Project
To add Spring Content REST to a Maven-based project, add the spring-content-rest artifact to your compile-time dependencies:
<dependencies>
...
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>${version}</version>
</dependency>
...
</dependencies>
2.5. Configuring Spring Content REST
To install Spring Content REST alongside your existing Spring MVC application, you need to include the appropriate MVC configuration. Spring Content REST configuration is defined in two classes called; RestConfiguration
and HypermediaConfiguration
and they can just be imported into your applications configuration.
Important
|
This step is unnecessary if you are using Spring Boot’s auto-configuration. Spring Boot will automatically enable Spring Content REST when you include com.github.paulcwarren:spring-content-rest-boot-starter and your app is flagged as a @SpringBootApplication .
|
Make sure you also configure Spring Content stores for the store you wish to use. For details on that, please consult the reference documentation for the corresponding Spring Content module.
3. Store Resources
3.1. Fundamentals
The core functionality of Spring Content REST, enabled through @Import(RestConfiguration.class)
, is to export resources
for Spring Content stores. This is often closely related to Spring Data repositories.
The following describes typical store scenarios and how they are exported with Spring Content REST.
3.1.1. Resources
Spring Content Stores manage Spring Resources that, when exported using Spring Content REST, are accessible by REST endpoint.
Consider the following Store interface:
public interface DvdStore extends Store<String> {}
In this example, the Store’s Resources are exported to the URI /dvds
. The path is derived from the
uncapitalized, pluralized, simple class name of the interface. When interacting with this endpoint any additional path
is deemed to be the Resource’s location and will be used to fetch the Resource using the Store’s getResource
method.
For example, a GET request to /dvds/comedy/monty_pythons_flying_circus.mp4
will fetch from the DvdStore
(/dvds
),
the Resource /comedy/monty_pythons_flying_circus.mp4
.
3.1.2. Entity Resources
Entity Resources are Spring Resource associated with Spring Data Entities.
Assume the following Entity
class, Repository
and Store
interfaces:
@Entity
public class Dvd {
@Id
private Long id;
@ContentId
private UUID contentId;
@ContentLength
private Long contentLength;
@MimeType
private String mimeType;
// getters and setters
}
public interface DvdRepository extends CrudRepository<Dvd, Long> {}
public interface DvdStore extends ContentStore<Dvd, UUID> {}
In this example a single Spring Resource (the DVD’s video stream) is associated with a Dvd Entity by annotating additional
fields on the Entity using the @ContentId
, @ContentLength
and @MimeType
annotations. In this example Spring Data
REST exports a collection resource to /dvds
. The path is derived from the uncapitalized, pluralized, simple class
name of the domain class. Item resources are also exported to the URI /dvds/{id}
. The HTTP methods used to request
this endpoint map onto the methods of CrudRepository
.
Similarly, Spring Content REST also exports any associated Spring Resources to the URI /dvds/{id}
. In this case the
HTTP methods map onto the methods of ContentStore
as follows:-
-
GET → getContent
-
POST/PUT → setContent
-
DELETE → unsetContent
3.1.3. Property Resources
Property Resources are associated with the properties of Spring Data Entities, that themselves maybe Spring Data Entities. This allows many Resources to be associated with a single Entity.
Consider the following Entity
model, with Repository
and Store
interfaces:
@Entity
public class Dvd {
private @Id @GeneratedValue Long id;
private String title;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "image_id")
private Image image;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "stream_id")
private Stream stream;
...
}
@Entity
public class Image {
// Spring Data managed attribute
private @Id @GeneratedValue Long id;
@OneToOne
private Dvd dvd;
// Spring Content managed attributes
private @ContentId UUID contentId;
private @ContentLength Long contentLen;
}
@Entity
public class Stream {
// Spring Data managed attribute
private @Id @GeneratedValue Long id;
@OneToOne
private Dvd dvd;
// Spring Content managed attributes
private @ContentId UUID contentId;
private @ContentLength Long contentLen;
}
public interface DvdRepository extends CrudRepository<Dvd, Long> {}
public interface ImageStore extends ContentStore<Image, UUID> {}
public interface StreamStore extends ContentStore<Stream, UUID> {}
In this example separate Resources are associated with the image
and stream
properties of the Dvd
Entity.
When using JPA and its relational characteristics these associations are typically (but not always) also Entity associations as well, as shown here. However when using NoSQL databases like MongoDB that are capable of storing hierarchical data they are true property associations.
As before, Spring Data REST will export an item resource under the URI /dvds/{id}
. However, this time Spring Content
REST will export Property Resources to the URI /dvds/{id}/image/{contentId}
and /dvds/{id}/stream/{contentId}
managed by the respective Store. A property can be set by POSTing to /dvds/{id}/image
(or /dvds/{id}/stream
).
Note
|
as these properties are both single values these Resources are also available under the simplified URI
/dvds/{id}/image and /dvds/{id}/stream where the {contentId} is omitted for convenience.
|
3.1.4. Property Collection Resources
Property Resources can also target Collections.
Consider the following example:-
@Entity
public class Dvd {
private @Id @GeneratedValue Long id;
private String title;
@OneToMany
@JoinColumn(name = "chapter_id")
private List<Chapter> chapters;
...
}
@Entity
public class Chapter {
// Spring Data managed attribute
private @Id @GeneratedValue Long id;
// Spring Content managed attributes
private @ContentId UUID contentId;
private @ContentLength Long contentLen;
}
public interface DvdRepository extends CrudRepository<Dvd, Long> {}
public interface ChapterStore extends ContentStore<Chapter, UUID> {}
In this example many Resources can be associated with the chapters
property of the Dvd
Entity.
As with Property Resources, Property Collection Resources are also exported to the URI /dvds/{id}/chapters
.
However, in this case, POSTing to /dvds/{id}/chapters
always appends a new Resource to the 'Chapters' Collection.
This Resource supports both POST and PUT HTTP methods.
3.1.5. Search
Exported content stores may be marked as Searchable
. Assume the following content store interface:
public interface DvdStore extends ContentStore<Dvd, UUID>, Searchable<UUID> {}
When exported Spring Content REST exposes a fulltext query resource for each Searchable
method. These resources are
exported under the URI /dvds/searchContent/<findMethod>
. Method parameters are supplied as query parameters:
curl http://localhost:8080/dvds/searchContent/findKeywords?keyword=one&keyword=two
3.1.6. Default status codes
For the content resources exposed, we use a set of default status codes:
-
200 OK - for plain GET requests and POST and PUT requests that overwrite existing content resources
-
201 Created - for POST and PUT requests that create new content resources
-
204 No Content - for DELETE requests
-
206 Partial Content - for range GET requests
3.1.7. Resource Discoverability
A core principle of HATEOAS is that Resources should be discoverable through the publication of links that point to the available resources. There are a few competing de-facto standards specifying how to represent links in JSON. By default, Spring Data REST uses HAL to render responses. HAL defines links to be contained in a property of the returned document.
Resource discovery starts at the top level of the application. By issuing a request to the root URL under which the Spring Data REST application is deployed, the client can extract a set of links from the returned JSON object that represent the next level of resources that are available to the client.
When enabled through @Import(HypermediaConfiguration.class)
Spring Content REST will inject Store, Entity and Property
Resources links for both into the HAL responses created by Spring Data REST.
3.2. The Store Resource
Spring Content REST exports Store Resources to /{store}/**
. The resource path and linkrel can be customized using the
@StoreRestResource
annotation on the Store interface.
3.2.1. Supported HTTP Methods
Store Resources support GET
, PUT
, POST
, and DELETE
. All other HTTP methods will cause a 405 Method Not
Allowed
.
GET
Returns the Resource’s content
Supported media types
All content types except application/json
PUT/POST
Sets the Resources’s content
Supported media types
All content types except application/json
DELETE
Removes the Resource’s content
Supported media types
All content types except application/json
3.3. The Entity Resource
Spring Content REST exports Entity Resources to /{store}/{id}
. The resource path can be customized using the
@StoreRestResource
annotation on the Store interface.
3.3.1. Supported HTTP Methods
Entity Resources support GET
, PUT
, POST
, and DELETE
. All other HTTP methods will cause a 405 Method Not
Allowed
.
GET
Returns the Resource’s content
Supported media types
All content types except application/json
PUT/POST
Sets the Resources’s content
Supported media types
All content types except application/json
DELETE
Removes the Resource’s content
Supported media types
All content types except application/json
3.4. The Property Resource
Spring Content REST exports Property Resources to /{store}/{id}/{property}
and /{store}/{id}/{property}/{contentId}
.
The resource path can be customized using the @StoreRestResource
annotation on the Store interface.
3.4.1. Supported HTTP Methods
Property Resources support GET
, PUT
, POST
, and DELETE
. All other HTTP methods will cause a 405 Method Not
Allowed
.
GET
Returns the Resource’s content
Supported media types
All content types except application/json
PUT/POST
Sets the Resources’s content
Supported media types
All content types except application/json
DELETE
Removes the Resource’s content
3.5. The Collection Property Resource
Spring Content REST exports Property Collection Resources to /{store}/{id}/{property}
. The resource path can be
customized using the @StoreRestResource
annotation on the Store interface.
3.5.1. Supported HTTP Methods
Content collection resources support PUT
and POST
.
PUT/POST
Appends new content to the collection of Resources
Supported media types
All content types except application/json
4. Repository Resources (Spring Data REST Extensions)
4.1. Search
4.1.1. The SearchContent Resource
When a repository extending Searchable
is exported a findKeyword
endpoint will be
available at the /{repository}/searchContent/findKeyword
URI.
curl http://localhost:8080/searchContent/findKeyword?keyword=foo
Supported HTTP Methods
As the SearchContent resource is read-only it supports GET
only. All other HTTP methods will
cause a 405 Method Not Allowed
.
Supported media types
-
application/hal+json
-
application/json
.
4.2. Locking and Versioning
4.2.1. The Lock Resource
When a repository extending LockingAndVersioningRepository
is exported a lock endpoint
will be available at the /{repository}/{id}/lock
URI.
curl -X PUT http://localhost:8080/docs/1234/lock
curl -X DELETE http://localhost:8080/docs/1234/lock
Supported HTTP Methods
The Lock resource supports PUT
and DELETE
. All other HTTP methods will cause a
405 Method Not Allowed
.
PUT
Acquires a pessimistic lock on the resource.
DELETE
If held, releases the pessimistic lock on the resource.
Not applicable.
4.2.2. The Version Resource
When a repository extending LockingAndVersioningRepository
is exported a version
endpoint will be available at the /{repository}/{id}/version
URI.
curl -X PUT http://localhost:8080/docs/1234/version -d '{"number": "1.1", "label": "a minor change"}'
Supported HTTP Methods
The version resource supports PUT
. All other HTTP methods will cause a 405 Method
Not Allowed
.
PUT
Creates a new version of the entity.
application/json
4.2.3. The FindAllVersionsLatest Resource
When a repository extending LockingAndVersioningRepository
is exported a findAllVersionsLatest
endpoint will be available at the /{repository}/findAllVersionsLatest
URI.
curl -X GET http://localhost:8080/docs/findAllVersionsLatest
Supported HTTP Methods
The version resource supports GET
. All other HTTP methods will cause a 405 Method
Not Allowed
.
GET
Returns the latest version of all entities.
Not applicable.
4.2.4. The FindAllVersions Resource
When a repository extending LockingAndVersioningRepository
is exported a findAllLatestVersions
endpoint will be available at the /{repository}/{id}/findAllVersions
URI.
curl -X GET http://localhost:8080/docs/1234/findAllVersions
Supported HTTP Methods
The version resource supports GET
. All other HTTP methods will cause a 405 Method
Not Allowed
.
GET
Returns all versions of the given entity.
Not applicable.
5. Customizing Store Resources
5.1. Configuring CORS
For security reasons, browsers prohibit AJAX calls to resources residing outside the current origin. When working with client-side HTTP requests issued by a browser you may want to enable specific HTTP resources to be accessible.
Spring Data Content supports Cross-Origin Resource Sharing (CORS) through Spring’s CORS support.
5.1.1. Store Interface CORS Configuration
You can add a @CrossOrigin annotation to your store interfaces to enable CORS for the whole store. By default @CrossOrigin allows all origins and HTTP methods:
@CrossOrigin
interface DvdStore extends ContentStore<Dvd, UUID> {}
In the above example CORS support is enabled for the whole DvdStore. @CrossOrigin also provides attributes to perform more granular configuration.
@CrossOrigin(origins = "http://mydomain.com",
methods = { RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE },
maxAge = 3600)
interface DvdStore extends ContentStore<Dvd, UUID> {}
This example enables CORS support for the whole DvdStore providing one origin, restricted to GET, POST and DELETE methods with a max age of 3600 seconds.
5.1.2. Global CORS Configuration
In addition to fine-grained, annotation-based configuration, you probably want to define some global CORS configuration as well. This is similar to Spring Web MVC’S CORS configuration but can be declared within Spring Content REST and combined with fine-grained @CrossOrigin configuration. By default, all origins and GET, HEAD, and POST methods are allowed.
The following example sets up the same CORS configuration but as a global configuration:
@Configuration
public class SpringContentRestCustomization {
@Bean
private ContentRestConfigurer contentRestConfigurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config.getCorsRegistry().addMapping("/dvds/**")
.allowedMethods("GET", "POST", "DELETE")
.allowedOrigins("http://mydomain.com")
.maxAge(3600);
}
};
}
}
5.2. Changing the Base URI
By default, Spring Content REST serves up REST resources at the root URI, '/'.
With Spring Boot 1.2 and later versions, you can change the base URI by setting a single property in application.properties, as follows:
spring.content.rest.baseUri=/api
Or if you are not using Spring Boot, you can do the following:
@Configuration
class CustomContentRestMvcConfiguration {
@Bean
public ContentRestConfigurer contentRestConfigurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config.setBaseUri(URI.create("/contentApi"));
}
};
}
}
5.3. Changing the link relation
For each exported Spring Resource, Spring Content REST will generate a suitable linkrel.
However, it can sometimes be useful to control this yourself.
Given the following example:
@Entity
public class Dvd {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ContentId
private UUID contentId;
@ContentLength
private Long contentLength;
@MimeType
private String mimeType;
// getters and setters
}
public interface DvdRepository extends CrudRepository<Dvd, Long> {}
public interface DvdStore extends ContentStore<Dvd, UUID> {}
Spring Content REST will export the Spring Resource to the following linkrel:
"_links" : {
"self" : {
...
},
"dvd" : {
...
},
"dvds" : {
"href" : "http://localhost:8080/dvds/1"
}
}
Specifying a linkRel
on the StoreRestResource, as follows:
@StoreRestResource(linkRel="content")
public interface DvdStore extends ContentStore<Dvd, UUID> {}
will result in the following linkrel instead:
"_links" : {
"self" : {
...
},
"dvd" : {
...
},
"content" : {
"href" : "http://localhost:8080/dvds/1"
}
}
5.4. Fully Qualified Links
By default, and where possible, Spring Content REST exports Spring Resources to shortened link URIs. These will often match the Spring Data Rest Entity URI.
Given the following example:
@Entity
public class Dvd {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ContentId
private UUID contentId;
@ContentLength
private Long contentLength;
@MimeType
private String mimeType;
// getters and setters
}
public interface DvdRepository extends CrudRepository<Dvd, Long> {}
public interface DvdStore extends ContentStore<Dvd, UUID> {}
As there is only a single associated Spring Resource, Spring Content REST will generate the following URI:
"_links" : {
"self" : {
...
},
"dvd" : {
...
},
"dvds" : {
"href" : "http://localhost:8080/dvds/1"
}
}
To generate fully qualified link URIs set the following property:
spring.content.rest.fullyQualifiedLinks=true
Or if you are not using Spring Boot, you can do the following:
@Configuration
class CustomContentRestMvcConfiguration {
@Bean
public ContentRestConfigurer contentRestConfigurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config.setFullyQualifiedLinks(true);
}
};
}
}
Spring Content REST will now generate links as follows:
"_links" : {
"self" : {
...
},
"dvd" : {
...
},
"content" : {
"href" : "http://localhost:8080/dvds/1/content"
}
}
where content
is the extracted property name taken from the field contentId
.