Bonjour à tous,
Tout est résumé dans le titre.
Je fabrique une API pour un forum de rencontres entre musiciens.
Mon projet Spring REST est basé sur celui ci:
https://spring.io/guides/tutorials/rest/
Au détail près que j'ai une base de données Postgres, et que tout est sur Docker (API + BDD) avec Docker Compose.
VOICI LA STRUCTURE DU PROJET;
VOICI LE CODE POUR UNE ENTITE:
ENTITE:
CONTROLLER:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 package com.praline40.MeetZicker.Style; import com.praline40.MeetZicker.Group.Group; import com.praline40.MeetZicker.Musician.Musician; import javax.persistence.*; import java.util.ArrayList; import java.util.List; @Entity public class Style { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(mappedBy = "styles") private List<Musician> musicians = new ArrayList<>(); @ManyToMany(mappedBy = "styles") private List<Group> groups = new ArrayList<>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public List<Musician> getMusicians() { return musicians; } public void setMusicians(List<Musician> musicians) { this.musicians = musicians; } public List<Group> getGroups() { return groups; } public void setGroups(List<Group> groups) { this.groups = groups; } }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 package com.praline40.MeetZicker.Style; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.mediatype.problem.Problem; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @RestController //Combination of @Controller and @ResponseBody. Beans returned are converted to/from JSON/XML. public class StyleController { //@Autowired // Autowire the StyleRepository so that we can retrieve and save data to database. private final StyleRepository repository; private final StyleModelAssembler assembler; // Injecting StyleModelAssembler into the controller StyleController(StyleRepository repository, StyleModelAssembler assembler) { this.repository = repository; this.assembler = assembler; } @GetMapping("/styles/{id}") EntityModel<Style> one(@PathVariable Long id) { Style style = repository.findById(id) // .orElseThrow(() -> new StyleNotFoundException(id)); return assembler.toModel(style); } @GetMapping("/allstyles") public String allStyles(){ return "You wanna know all styles !!"; } @GetMapping("/styles") CollectionModel<EntityModel<Style>> all() { List<EntityModel<Style>> styles = repository.findAll().stream() .map(assembler::toModel) .collect(Collectors.toList()); return CollectionModel.of(styles, linkTo(methodOn(StyleController.class).all()).withSelfRel()); } @PostMapping("/styles") ResponseEntity<?> newStyle(@RequestBody Style newStyle) { EntityModel<Style> entityModel = assembler.toModel(repository.save(newStyle)); return ResponseEntity // .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) // .body(entityModel); } @PutMapping("/styles/{id}") ResponseEntity<?> replaceStyle(@RequestBody Style newStyle, @PathVariable Long id) { Style updatedStyle = repository.findById(id) // .map(style -> { style.setName(newStyle.getName()); style.setMusicians(newStyle.getMusicians()); style.setGroups(newStyle.getGroups()); return repository.save(style); }) // s'il n'existe pas déjà on le créé .orElseGet(() -> { newStyle.setId(id); return repository.save(newStyle); }); EntityModel<Style> entityModel = assembler.toModel(updatedStyle); return ResponseEntity // .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) // .body(entityModel); } @DeleteMapping("/styles/{id}") ResponseEntity<?> deleteStyle(@PathVariable Long id) { repository.deleteById(id); return ResponseEntity.noContent().build(); } }
ASSEMBLER:
NOT FOUND EXCEPTIONS:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.praline40.MeetZicker.Style; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.server.RepresentationModelAssembler; import org.springframework.stereotype.Component; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @Component public class StyleModelAssembler implements RepresentationModelAssembler<Style, EntityModel<Style>> { @Override public EntityModel<Style> toModel(Style style) { return EntityModel.of(style, // linkTo(methodOn(StyleController.class).one(style.getId())).withSelfRel(), linkTo(methodOn(StyleController.class).all()).withRel("styles")); } }
REPOSTITORY:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 package com.praline40.MeetZicker.Style; class StyleNotFoundException extends RuntimeException { StyleNotFoundException(Long id) { super("Could not find style " + id); } }
DOCKERFILE:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 package com.praline40.MeetZicker.Style; import org.springframework.data.jpa.repository.JpaRepository; interface StyleRepository extends JpaRepository<Style, Long> { }
DOCKER-COMPOSE:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 FROM openjdk:11 ADD target/meetzicker.jar meetzicker.jar ENTRYPOINT ["java", "-jar","meetzicker.jar"] EXPOSE 8080
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 version: '2.1' services: API: image: 'meetzicker.jar:latest' ports: - "8080:8080" depends_on: db: condition: service_healthy environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/${POSTGRES_DB} - SPRING_DATASOURCE_USERNAME=${POSTGRES_USER} - SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD} - SPRING_JPA_HIBERNATE_DDL_AUTO=update db: image: postgres:${POSTGRES_VERSION} ports: - "5432:5432" environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_DB=${POSTGRES_DB} healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5
DANS POSTMAN:
D'abord un post qui fonctionne:
Ensuite un get qui crashe:
GET http://localhost:8080/styles
NB: tant que je ne fais pas de POST, le GET fonctionne (du moins pour les entités indépendantes)!
Un post pour un nouvel instrument:
Ensuite un get, pareil ça plante:
POUR CE CONTROLLER SIMPLIFIE PAS DE SOUCIS:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package com.praline40.MeetZicker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @RestController //Combination of @Controller and @ResponseBody. Beans returned are converted to/from JSON/XML. public class HomeController { @GetMapping("/") public String home(){ return "Welcome to Meetzicker !!"; } }
J'y connais pas grand chose, mais comme ça je dirais que cela vient d'un soucis entre mes relations entre entitées (relations base de données). Car pourquoi cela marcherait-il tant que la base de données est vide (avant POST) ?
J'avoue j'y suis allé de façon un peu bourrin j'ai fait directement une architecture relationnelle poussée.
Partager