Skip to content

Commit bfb4535

Browse files
joshlongphilwebb
authored andcommitted
Lesson 5: Giving Your Microservice a REST
1 parent 2db53a6 commit bfb4535

23 files changed

+561
-0
lines changed

livelessons-rest/README.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
:compat-mode:
2+
= Lesson 5: Giving Your Microservice a REST
3+
4+
_Why the web is a existence proof of the HTTP architecture and how to build
5+
services that exploit that._
6+
7+
- link:livelessons-rest-basic[Basic]
8+
- link:livelessons-rest-sync[Sync]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>livelessons</groupId>
7+
<artifactId>livelessons-rest</artifactId>
8+
<version>1.0.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>livelessons-rest-basic</artifactId>
11+
<properties>
12+
<main.basedir>../..</main.basedir>
13+
</properties>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>org.springframework.boot</groupId>
21+
<artifactId>spring-boot-starter-data-rest</artifactId>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-data-jpa</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>com.h2database</groupId>
29+
<artifactId>h2</artifactId>
30+
</dependency>
31+
</dependencies>
32+
</project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package demo;
2+
3+
import java.util.Arrays;
4+
5+
import org.springframework.boot.CommandLineRunner;
6+
import org.springframework.boot.SpringApplication;
7+
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
import org.springframework.context.annotation.Bean;
9+
10+
@SpringBootApplication
11+
public class Application {
12+
13+
@Bean
14+
CommandLineRunner commandLineRunner(PersonRepository personRepository) {
15+
return args -> {
16+
Arrays.asList("Phil", "Josh").forEach(name -> personRepository
17+
.save(new Person(name, (name + "@email.com").toLowerCase())));
18+
personRepository.findAll().forEach(System.out::println);
19+
};
20+
}
21+
22+
public static void main(String[] args) {
23+
SpringApplication.run(Application.class, args);
24+
}
25+
26+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package demo;
2+
3+
import javax.persistence.Entity;
4+
import javax.persistence.GeneratedValue;
5+
import javax.persistence.Id;
6+
7+
@Entity
8+
public class Person {
9+
10+
@Id
11+
@GeneratedValue
12+
private Long id;
13+
14+
private String name;
15+
16+
private String email;
17+
18+
Person() {
19+
}
20+
21+
public Person(String name, String email) {
22+
this.name = name;
23+
this.email = email;
24+
}
25+
26+
public Long getId() {
27+
return this.id;
28+
}
29+
30+
public String getName() {
31+
return this.name;
32+
}
33+
34+
public String getEmail() {
35+
return this.email;
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return "Person{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email
41+
+ '\'' + '}';
42+
}
43+
44+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package demo;
2+
3+
import java.io.File;
4+
import java.io.FileNotFoundException;
5+
import java.io.FileOutputStream;
6+
import java.net.URI;
7+
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.beans.factory.annotation.Value;
10+
import org.springframework.core.io.FileSystemResource;
11+
import org.springframework.core.io.Resource;
12+
import org.springframework.http.HttpHeaders;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.MediaType;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.util.Assert;
17+
import org.springframework.util.FileCopyUtils;
18+
import org.springframework.web.bind.annotation.PathVariable;
19+
import org.springframework.web.bind.annotation.RequestMapping;
20+
import org.springframework.web.bind.annotation.RequestMethod;
21+
import org.springframework.web.bind.annotation.RequestParam;
22+
import org.springframework.web.bind.annotation.RestController;
23+
import org.springframework.web.multipart.MultipartFile;
24+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
25+
26+
@RestController
27+
@RequestMapping("/people/{id}/photo")
28+
public class PersonPhotoRestController {
29+
30+
private File root;
31+
32+
@Autowired
33+
private PersonRepository personRepository;
34+
35+
@Value("${user.home}")
36+
public void setUserHome(String home) {
37+
this.root = new File(home, "Desktop/images");
38+
Assert.isTrue(this.root.exists() || this.root.mkdirs(),
39+
"The path '" + this.root.getAbsolutePath() + "' must exist.");
40+
}
41+
42+
@RequestMapping(method = RequestMethod.GET)
43+
public ResponseEntity<Resource> read(@PathVariable Long id) throws Exception {
44+
Person person = this.personRepository.findOne(id);
45+
File file = fileFor(person);
46+
if (!file.exists()) {
47+
throw new FileNotFoundException(file.getAbsolutePath());
48+
}
49+
HttpHeaders httpHeaders = new HttpHeaders();
50+
httpHeaders.setContentType(MediaType.IMAGE_JPEG);
51+
Resource resource = new FileSystemResource(file);
52+
return new ResponseEntity<>(resource, httpHeaders, HttpStatus.OK);
53+
}
54+
55+
@RequestMapping(method = { RequestMethod.POST, RequestMethod.PUT })
56+
public ResponseEntity<?> write(@PathVariable Long id,
57+
@RequestParam MultipartFile file) throws Exception {
58+
Person person = this.personRepository.findOne(id);
59+
FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(fileFor(person)));
60+
URI location = ServletUriComponentsBuilder.fromCurrentRequest().buildAndExpand(id)
61+
.toUri();
62+
return ResponseEntity.created(location).build();
63+
}
64+
65+
private File fileFor(Person person) {
66+
return new File(this.root, Long.toString(person.getId()));
67+
}
68+
69+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package demo;
2+
3+
import java.util.Collection;
4+
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.repository.query.Param;
7+
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
8+
9+
@RepositoryRestResource(path = "people")
10+
interface PersonRepository extends JpaRepository<Person, Long> {
11+
12+
Collection<Person> findByEmail(@Param("email") String e);
13+
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package demo;
2+
3+
import org.springframework.hateoas.Link;
4+
import org.springframework.hateoas.Resource;
5+
import org.springframework.hateoas.ResourceProcessor;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
8+
import org.springframework.web.util.UriComponents;
9+
10+
@Component
11+
public class PersonResourceProcessor implements ResourceProcessor<Resource<Person>> {
12+
13+
@Override
14+
public Resource<Person> process(Resource<Person> resource) {
15+
String id = Long.toString(resource.getContent().getId());
16+
UriComponents uriComponents = ServletUriComponentsBuilder.fromCurrentContextPath()
17+
.path("/people/{id}/photo").buildAndExpand(id);
18+
String uri = uriComponents.toUriString();
19+
resource.add(new Link(uri, "photo"));
20+
return resource;
21+
}
22+
23+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package demo;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
5+
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
6+
7+
@Configuration
8+
public class SimpleRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
9+
10+
@Override
11+
protected void configureRepositoryRestConfiguration(
12+
RepositoryRestConfiguration config) {
13+
config.exposeIdsFor(Person.class);
14+
}
15+
16+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
insert into person( name, email) values ( 'josh', '[email protected]');
2+
insert into person( name, email) values ( 'john', '[email protected]');
3+
insert into person( name, email) values ( 'yoana', '[email protected]');
4+
insert into person( name, email) values ( 'emo', '[email protected]');
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>livelessons</groupId>
7+
<artifactId>livelessons-rest</artifactId>
8+
<version>1.0.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>livelessons-rest-errors</artifactId>
11+
<properties>
12+
<main.basedir>../..</main.basedir>
13+
</properties>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>org.springframework.boot</groupId>
21+
<artifactId>spring-boot-starter-data-rest</artifactId>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.springframework.hateoas</groupId>
25+
<artifactId>spring-hateoas</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.plugin</groupId>
29+
<artifactId>spring-plugin-core</artifactId>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot-starter-data-jpa</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>com.h2database</groupId>
37+
<artifactId>h2</artifactId>
38+
</dependency>
39+
</dependencies>
40+
</project>

0 commit comments

Comments
 (0)