Web Service using Mongo DB
Under construction |
Preparing Environment
Launch Mongo and Config Server:
$ docker run --name mongo -d -p 27017:27017 mongo $ docker run -d --name=config-service -p 8888:8888 msvcdojo/config-service:0.0.1
Building Profiles Service
Create a basic web Service with the name profiles-service.
In the build.gradle add additional dependency:
compile("org.springframework.boot:spring-boot-starter-data-rest:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion")
compile("org.springframework.hateoas:spring-hateoas")
Under construction |
Add a Profile
entity describing a document that will be stored in the Database:
@Document(collection="profiles")
class Profile {
public Profile() {
}
@Id
private String id;
@Indexed
private String fullName;
private List<String> photos;
public void setId(String id) {
this.id = id;
}
public void setKey(String key) {
this.id = key;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public void addPhotoReference(String photoId) {
this.photosList().add(photoId);
}
public String getKey() {
return id;
}
public String getFullName() {
return fullName;
}
public Integer getPhotoCount() { return this.photosList().size(); }
public List<String> photosList() {
if (this.photos == null)
this.photos = new ArrayList<>();
return this.photos;
}
@Override
public String toString() {
return String.format(
"Customer[id=%s, fullName='%s']",
id, fullName);
}
}
Add a PhotoResource. We’re going to store binary data in the "files" section of MongoDB.
class PhotoResource extends AbstractResource {
private final Photo photo;
public PhotoResource(Photo photo) {
Assert.notNull(photo, "Photo must not be null");
this.photo = photo;
}
@Override
public String getDescription() {
return null;
}
@Override
public InputStream getInputStream() throws IOException {
return this.photo.getInputStream();
}
@Override
public long contentLength() throws IOException {
return -1;
}
}
interface Photo {
/**
* @return a new {@link InputStream} containing photo data as a JPEG. The caller is
* responsible for closing the stream.
* @throws IOException
*/
public InputStream getInputStream() throws IOException;
}
Now it’s time to add a data repository description with it’s "verbs":
@RepositoryRestResource
interface ProfilesRepository extends MongoRepository<Profile, String> {
@Query("{ '_id' : ?0 }")
Profile findByKey(@Param("key") String key);
Profile findByFullName(@Param("address") String address);
}
The repository is annotated with @RepositoryRestResource
making it a REST controller
out of the box. This exposes, in part, GET method to retrieve a list of stored items and
a POST method to insert new items.
We’re going to add a REST controller that will allow us to insert a photo (large
binary file) with any arbitrary key by posting data into the URL that looks like
this: http://host/profiles/{key}/photos
Create a rest controller with the mapping /profiles/{key}/photos
:
@RestController
@RequestMapping(value = "/profiles/{key}/photos", produces = MediaType.APPLICATION_JSON_VALUE)
class ProfilePhotoController {
Add a handler that will process POST requests:
@RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
ResponseEntity<Resource<Profile>> insertPhoto(@PathVariable String key,
@RequestParam MultipartFile file) throws IOException {
Photo photo = file::getInputStream;
Profile profile = this.profilesRepository.findOne(key);
String id = key + profile.getPhotoCount();
try (InputStream inputStream = photo.getInputStream()) {
this.fs.store(inputStream, id);
}
profile.addPhotoReference(id);
this.profilesRepository.save(profile);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}/photo").buildAndExpand(id).toUri();
HttpHeaders headers = new HttpHeaders();
headers.setLocation(uri);
return new ResponseEntity<>(
this.readPhoto(key), headers, HttpStatus.CREATED);
}
Follow the solution further …
For demo purposes only our service is going to clean up data on start. |
The cleanup is executed by CommandLineRunner
lambda:
@Bean
CommandLineRunner init(ProfilesRepository profilesRepository) {
return a -> profilesRepository.deleteAll();
}
Play time
Let’s add some data. One profile and one photo for that profile:
$ gradlew clean prepDocker
$ java -jar -Dconfig-service.uri=http://dockerhost:8888 -Dmongoserver=dockerhost build\libs\profiles-service-0.0.1.jar
$ echo { "key":"john", "fullName":"John Smith" } | curl -H "Content-Type: application/json" -d @- http://localhost:8101/profiles
{{
"fullName" : "John Smith",
"key" : "john",
"photoCount" : 0,
"_links" : {
"self" : {
"href" : "http://localhost:8101/profiles/john"
},
"profile" : {
"href" : "http://localhost:8101/profiles/john"
},
"photos" : {
"href" : "http://localhost:8101/profiles/john/photos"
}
}
}
$ curl -F "file=@../../../misc/face.png" http://localhost:8101/profiles/john/photos
{
"fullName" : "John Smith",
"key" : "john",
"photoCount" : 1
}
Let’s check what photos are stored for a profile:
$ curl http://localhost:8101/profiles/john/photos [ { "content" : "john0", "links" : [ { "rel" : "john0", "href" : "http://localhost:8101/profiles/john/photos/john0/photo" } ] } ]
Navigate in your browser of choice to the URL provided by the last response and you should see the uploaded picture.
Bonus
-
Explore the contents of MongoDB by launching Mongo Express web client in docker and navigating to
http://dockerhost:8081
:
$ docker run -d -p 8081:8081 --link mongo:mongo knickers/mongo-express