Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Apprendre à développer les services REST avec Spring Boot et Spring RestTemplate,
Un tutoriel de Bertrand Nguimgo

Le , par parchemal

33PARTAGES

9  0 
Bonjour chers amis développeurs,

J'ai rédigé ce tutoriel qui permet d'apprendre à développer les services REST avec Spring Boot et Spring RestTemplate. L'idée dans ce tutoriel consiste à développer les services REST client/serveur dans deux applications distinctes et surtout à montrer qu'avec Spring Boot, le développeur ne passe plus trop du temps de la configuration de son projet, et peut ainsi déployer très facilement son application dans un environnement de développement comme dans un environnement de production.

Cet espace vous permet de donner votre point de vue et éventuellement une autre façon de répondre au même besoin. Votre participation est très attendue.

Merci d'avance
Bertrand Nguimgo

Retrouvez les meilleurs cours et tutoriels pour apprendre Spring

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de eulbobo
Membre chevronné https://www.developpez.com
Le 08/03/2018 à 16:55
Citation Envoyé par parchemal Voir le message
Je vais voir comment mettre en place le repo git. As-tu déjà mis en place un repo git, si oui quelques indications ?

Bertrand
Le plus simple :
- Aller sur github : github.com
- Se créer un compte
- Créer un repository public (c'est gratuit tant que c'est public)
- Suivre les instruction pour pousser du code sur le repo
- Donner l'URL aux gens

Hésite pas à demander si tu as besoin d'aide, mais franchement le plus dur est de comprendre la finesse et la puissance de git, pas de créer un repo ou de partager du code :p
1  0 
Avatar de jeffray03
Membre chevronné https://www.developpez.com
Le 11/05/2018 à 15:18
salut,
le probleme est reglé,
c´etait juste une annotation qui a ete oublié sur :

Code : Sélectionner tout
1
2
3
4
@JsonIgnore
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}
et

Code : Sélectionner tout
1
2
3
4
@JsonIgnore
	public void setUsers(Set<User> users) {
		this.users = users;
	}
sinon apres tout marches bien.
Je continue la mise en pratique et je reviens vers vous.

Eric
1  0 
Avatar de jeffray03
Membre chevronné https://www.developpez.com
Le 13/05/2018 à 14:43
Salut,

je ne vois pas ou est ce que tu as definis l´entite (classe) Role.
Dans les entités User et Role , il n´ya pas d´implementation de la classe Comparable a laquelle tu fais reference dans le controller Usercontroller.

Dans le Test:
Code : Sélectionner tout
1
2
3
4
5
6
 
  @Test
    public void testFindByLogin() {
        User userFromDB = userRepository.findByLogin("user2");     
        assertThat("user2", is(userFromDB.getLogin()));//user2 a été créé lors de l'initialisation du fichier data.sql     
    }
Dans le fichier Data.sql il n´est pas mentioné la création de user2

Est ce un oublie?

Merci

Eric
1  0 
Avatar de vertex.3F
Membre confirmé https://www.developpez.com
Le 21/06/2018 à 17:42
bonjour,
petit a petit je continue avec ce tuto qui est vraiment riche. j'attaque les tests.

je vous fait part de la légère modification que j'ai effectuée dans les déclarations en haut de la classe de test UserServiceImplTest .

j'ai remplacé le pavé de code suivant

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
   @TestConfiguration //création des Beans nécessaires pour les tests
    static class UserServiceImplTestContextConfiguration {
 
        @Bean//bean de service
        public UserService userService () {
            return new UserServiceImpl();
        }
 
    	@Bean//nécessaire pour encrypter le mot de passe sinon échec des tests
    	public BCryptPasswordEncoder passwordEncoder() {
    		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    		return bCryptPasswordEncoder;
    	}
    }
par l'annotation @SpringBootTest

Voici ce que ça donne :
(NB : version dans pom.xml : springboot 2.0.2)

Code : Sélectionner tout
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
 
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
 
	private static Logger LOGGER = LoggerFactory.getLogger(UserServiceImplTest.class);
 
	@Autowired
	private UserService userService;
 
	@MockBean // création d'un mockBean pour UserRepository
	private UserRepository userRepository;
 
	@Test
	public void testGetAllUsers() {
 
		User user = new User("Dupont", "password", 1);
		Role role = new Role("USER_ROLE");// initialisation du role utilisateur
		Set<Role> roles = new HashSet<>();
		roles.add(role);
		user.setRoles(roles);
		List<User> allUsers = Arrays.asList(user);
		Mockito.when(userRepository.findAll()).thenReturn(allUsers);
 
		Collection<User> users = userService.getAllUsers();
		assertNotNull(users);
		assertEquals(users, allUsers);
		assertEquals(users.size(), allUsers.size());
 
		Mockito.verify(userRepository).findAll();
	}
...
}
pas de contrepartie au niveau configuration; ça fonctionne très bien d'emblée; y'a t il un inconvénient quelque part ? je n'en ai pas remarqué mais si quelqu'un a des commentaires je suis preneur evidemment.
merci
bonne journée
1  0 
Avatar de momjunior
Membre actif https://www.developpez.com
Le 30/05/2019 à 21:42
Ok je viens de comprendre, c'est le fichier application-dev-properties qui est chargé en production grâce à cette ligne:

Code : Sélectionner tout
spring.profiles.active=dev
contenue dans le fichier application.properties, c'est bien ça?
1  0 
Avatar de momjunior
Membre actif https://www.developpez.com
Le 29/07/2019 à 12:14
Citation Envoyé par Marc_3 Voir le message
Bonjour,

J'ai un souci et je cherche les sources java pour le tuto.

Mon problème est le suivant: J'ai pu implémenter en partie le Back-end, mais depuis un moment je ne peux plus l'atteindre avec mon browser pour envoyer les requêtes REST.

Chaque fois que j'envoie quelque chose je tombe sur l'écran pour login et pas moyen de me faire reconnaître.

Est-ce qu'il est possible de désactiver cette routine d'identification?

Je ne trouve pas la class: UserRepositoryImpl, contrairement à ce que dit le tuto la class n'a pas eté générée automatiquement.
Est-ce que l'on peut forcé SpringData à le faire??

Je ne trouve pas non plus la class: UserRegistrationForm, est-ce que ce serait possible d'avoir le code, cela m'aiderai à comprendre comment faire???

Merci de répondre
Bonjour

Pour les sources, regarde au niveau de:

I. Première partie : Le serveur
, ensuite
I-A. Introduction
et tu trouveras le lien à la dernière ligne du paragraphe :

Toutes les sources (client et serveur) sont disponibles en téléchargement
1  0 
Avatar de atha2
Membre éprouvé https://www.developpez.com
Le 30/10/2019 à 23:56
Bonjour et merci pour ce tutoriel très complet.

Une question, dans la partie I-B-1-b. Structure de l'application, on est d'accord qu'on présente d'abord le setup d'un application jar et ensuite le setup d'une application war ?
1  0 
Avatar de momjunior
Membre actif https://www.developpez.com
Le 27/01/2020 à 17:12
Bonjour

Il me semble qu'il y ait des problèmes de compatibilité concernant la méthode findOne et delete qui se trouvent dans UserController.java.

Dans le service User, la méthode findOne(Long) n'est plus disponible et j'ai lu dans les commentaires du tuto qu'il fallait la remplacer par findById et la signature devient "Optional<User> getUserById(Long id) throws BusinessResourceException;". Et ça marche.

Maintenant dans la Class UserController.java , je suis obligé de changer :

Code : Sélectionner tout
User userToUpdate = userService.getUserById(id);
par

Code : Sélectionner tout
Optional<User> userToUpdate = userService.getUserById(id);
Sauf que les méthodes getRoles, setLogin, setPassword, setActive, saveOrUpdateUser me génèrent des erreurs du genre:

The method getRoles() is undefined for the type Optional<User>


Ensuite concernant la méthode delete:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
	@Override
	@Transactional(readOnly=false)
	public void deleteUser(Long id) throws BusinessResourceException {
		try{
			userRepository.delete(id);
		}catch(Exception ex){
			throw new BusinessResourceException("Delete User Error", "Erreur de suppression de l'utilisateur avec l'identifiant: "+id, HttpStatus.INTERNAL_SERVER_ERROR);
		}		
	}
j'ai ce message d'erreur:

The method delete(User) in the type CrudRepository<User,Long> is not applicable for the arguments (Long)
Par quelle méthode dois-je la remplacer pour régler ce problème?

Merci
1  0 
Avatar de eulbobo
Membre chevronné https://www.developpez.com
Le 07/03/2018 à 11:21
Bonjour

J'ai parcouru ce tuto et je dois dire qu'il est très complet et très bien fait.

Mais j'ai quand même quelques questions d'enquiquineur ^^

- UserDTO : pourquoi créer un autre objet plutôt que d'utiliser directement l'objet User? (sachant qu'ils sont identiques, modulo le hashcode/equals)
Le pattern DTO oblige à sans arrêt passer d'un type d'objet à un autre juste pour obtenir une "pseudo" isolation des couches DAO/Services qui sont quand même liées les unes aux autres.
Accessoirement, ça se passe bien dans le cas présent parce qu'on ne gère pas de grappe d'objet.

- L'injection Spring : afin de faciliter les tests unitaires, il pourrait être bon de faire de l'autowire par constructeur plutôt que par propriété
Avec l'exemple de RoleServiceImpl
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
 
@Service(value = "roleService") // c'est optionnel de préciser le value s'il existe une seule implémentation de l'interface
public class RoleServiceImpl implements RoleService {
 
    private RoleRepository roleRepository;
 
    @Autowired
    public RoleServiceImpl(RoleRepository roleRepository){
        this.roleRepository = roleRepository;
    }
 
    ...
}
Ce qui permettrait de créer des tests unitaires sous la forme suivante :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
// TU pur : pas d'injection ou de dépendances autres que la classe testée
public class RoleServiceImplTest {
 
    @Test
    public void should_get_all_roles_when_asking_for_all_roles(){
        // ARRANGE
        RoleRepository roleRepository = Mockito.mock(RoleRepository.class);
        RoleServiceImpl roleService = new  RoleServiceImpl (roleRepository );
 
        Mockito.when(roleRepository.findAll().thenReturn(RoleFixture.getRoles());// classe RoleFixture permettant de créer des rôles à créer
        // ACT
        Collection<Role> result = roleService.getAllRoles();
 
        // ASSERT        
        Assertions.assertThat(result).isNotNull() // Assertions vient de assertJ qui est un excellent framework pour la rédaction de tests unitaires
                .hasSize(5)
                ....
    }
 
    ...
}
- Pourquoi est-ce que le code "métier" de la sauvegarde de l'utilisateur se trouve dans le contrôleur ?
Si les services ne servent que de "passe plat" pour les DAO, ils n'ont pas de valeur propre (à part des valeurs purement techniques comme la gestion des transaction et de l'encryptage des mots de passe)

- Un détail : c'est mieux quand les logger sont `private static final` (il manque le final là)

- Les requête POST/PUT sont en XML et le retour en JSON, ça fait bizarre non? C'est voulu pour montrer qu'on gère ce qu'on veut comme type de données?

- Les tests d'intégration avec TestRestTemplate : c'est TOP !

- Y'a un repo git de dispo avec le code source pour pouvoir faire des propositions d'amélioration?

Je le répète, c'est du chipotage, et le guide en l'état est très bien.
0  0 
Avatar de parchemal
Membre averti https://www.developpez.com
Le 07/03/2018 à 11:54
Bonjour Monsieur eulbobo,

Vous avez un regard très pointilleux, et je suis flatté de vous répondre. Merci pour vos remarques.

Citation Envoyé par eulbobo Voir le message
- UserDTO : pourquoi créer un autre objet plutôt que d'utiliser directement l'objet User?
Cette démarche a pour objectif de faire comprendre qu'il est préférable de manipuler un DTO que de manipuler l'objet lui-même, même si dans le cas présent, les deux sont strictement identiques.

Citation Envoyé par eulbobo Voir le message
@Service(value = "roleService") // c'est optionnel de préciser le value s'il existe une seule implémentation de l'interface
Tout à fait, mais de prudence, il faut le préciser pour faciliter la tâche à l'évolution future des différentes implémentations

Citation Envoyé par eulbobo Voir le message
- L'injection Spring : afin de faciliter les tests unitaires, il pourrait être bon de faire de l'autowire par constructeur plutôt que par propriété
Avec l'exemple de RoleServiceImpl
Très bonne remarque, super!!!!!, avantage donnée aux tests. Avec ça plus besoin d'injecter une instance de la classe à tester. Vous avez parfaitement raison. Merci encore

Citation Envoyé par eulbobo Voir le message
- Pourquoi est-ce que le code "métier" de la sauvegarde de l'utilisateur se trouve dans le contrôleur ?
Ça n'est pas normal. A revoir. Merci pour la remarque

Citation Envoyé par eulbobo Voir le message
- Un détail : c'est mieux quand les logger sont `private static final` (il manque le final là)
Erreur de frappe, vous avez un œil pointu, il faudra mettre à jour

Citation Envoyé par eulbobo Voir le message
- Les requête POST/PUT sont en XML et le retour en JSON, ça fait bizarre non? C'est voulu pour montrer qu'on gère ce qu'on veut comme type de données?
C'est fait express. Car c'est un des avantages de Spring Boot. Il sait se débrouiller tout seul. Et j'ai signalé ce mécanisme quelque part dans le tutoriel

J'ai tenu à automatiser les tests unitaires et les tests d'intégration comme vous l'avez si bien remarqué.

Bravo pour toutes vos remarques, je prendrais en compte ces remarques pour une prochaine mise à jour.

Cordialement
Bertrand Nguimgo
0  0