portaldacalheta.pt
  • Principal
  • Design De Marque
  • Personnes Et Équipes Produit
  • Innovation
  • Kpi Et Analyses
Science Des Données Et Bases De Données

Premiers pas avec les microservices: un didacticiel Dropwizard



Nous assistons tous à une augmentation de la popularité des architectures de microservices. Dans une architecture de microservices, Dropwizard occupe une place très importante. C'est un framework pour créer des services web RESTful ou, pour être plus précis, un ensemble d'outils et cadres pour créer des services Web RESTful.

exemple d'API de repos express node.js

Il permet aux développeurs un démarrage plus rapide du projet. Cela vous aide à empaqueter vos applications afin qu'elles puissent être facilement déployées dans un environnement de production en tant que services autonomes. Si vous avez déjà été dans une situation où vous devez démarrer un projet dans le cadre Spring Par exemple, vous savez probablement à quel point cela peut être douloureux.



Illustration: exemple de microservices dans le didacticiel Dropwizard



Avec Dropwizard, il suffit d'ajouter une dépendance Maven.



Dans ce blog, je vais vous guider tout au long du processus d'écriture d'un simple service Dropwizard RESTful. Lorsque nous aurons terminé, nous aurons un service pour les opérations CRUD de base en «parties». Peu importe ce qu'est la «partie»; Cela peut être n'importe quoi, mais c'est la première chose qui m'est venue à l'esprit.

Nous stockerons les données dans une base de données MySQL, en utilisant JDBI pour les consulter et nous utiliserons ce qui suit points de terminaison :



  • GET /parts -pour récupérer toutes les pièces de DB
  • GET /part/{id} pour obtenir une pièce particulière de DB
  • POST /parts -pour créer une nouvelle pièce
  • PUT /parts/{id} -pour éditer une pièce existante
  • DELETE /parts/{id} -pour supprimer la pièce d'un DB

Nous utiliserons OAuth pour authentifier notre service, puis y ajouterons des tests unitaires

Bibliothèques Dropwizard par défaut

Au lieu d'inclure toutes les bibliothèques nécessaires pour créer un service REST distinct et configurer chacune d'elles, Dropwizard le fait pour nous. Voici la liste des bibliothèques fournies par défaut avec Dropwizard:



  • Jetée: Vous aurez besoin de HTTP pour exécuter une application Web. Dropwizard intègre le conteneur servlet Jetty pour exécuter des applications Web. Au lieu de déployer vos applications sur un serveur d'applications ou un serveur Web, Dropwizard définit une méthode principale qui appelle le serveur Jetty en tant que processus autonome. Désormais, Dropwizard recommande d'exécuter l'application uniquement avec Jetty; d'autres services Web comme Tomcat ne sont pas officiellement pris en charge.
  • Jersey: Jersey est l'une des meilleures implémentations d'API REST du marché. De plus, il suit la spécification de la norme JAX-RS et constitue l'implémentation de référence pour la spécification JAX-RS. Dropwizard utilise Jersey comme cadre par défaut pour créer des applications Web RESTful.
  • Jackson: Jackson est la norme de facto pour gérer le format JSON. C'est l'une des meilleures API de mappage d'objets pour le format JSON.
  • Métrique: Dropwizard a son propre module de métriques pour exposer les métriques d'application via points de terminaison HTTP.
  • Goyave: En plus de structures de données hautement optimisées et immuables, Guava fournit un nombre croissant de classes pour accélérer le Développement Java .
  • Logback et Slf4j: Ces deux sont utilisés pour améliorer les mécanismes d'enregistrement.
  • Freemarker et moustache: Le choix des moteurs de modèles pour votre application est l'une des décisions clés. Le moteur de modèle choisi doit être plus flexible pour écrire de meilleurs scripts. Dropwizard utilise à la fois des moteurs de modèles Freemarker et Moustache bien connus et populaires pour créer des interfaces utilisateur.

Outre la liste ci-dessus, il existe de nombreuses autres bibliothèques telles que Joda Time, Liquibase, Apache HTTP Client et Hibernate Validator utilisées par Dropwizard pour créer des services REST.

Configuration Maven

Dropwizard prend officiellement en charge Maven . Même si vous pouvez utiliser d'autres outils de construction, la plupart des guides et de la documentation utilisent Maven, nous allons donc l'utiliser également ici. Si vous ne connaissez pas Maven, vous pouvez vous y référer Tutoriel Maven .



Il s'agit de la première étape de la création de votre application Dropwizard. Ajoutez l'entrée suivante au fichier pom.xml depuis Maven:

io.dropwizard dropwizard-core ${dropwizard.version}

Avant d'ajouter l'entrée précédente, vous pouvez ajouter dropwizard.versión comme indiqué dans ce qui suit:



1.1.0

C'est tout. Vous avez terminé d'écrire la configuration Maven. Cela téléchargera toutes les dépendances nécessaires pour votre projet. La version actuelle de Dropwizard est 1.1.0 , nous allons donc l'utiliser dans ce guide.

Nous pouvons maintenant passer à l'écriture de notre première vraie application Dropwizard.



Définir la classe de configuration

Dropwizard stocke les paramètres dans des fichiers YAML . Vous aurez besoin du fichier configuration.yml dans le dossier racine de l'application. Ce fichier sera désérialisé en une instance de la classe de configuration de votre application et validé. Le fichier de configuration de votre application est la sous-classe de la classe de configuration Dropwizard (io.dropwizard.Configuration).

Créons une classe de configuration simple:

import javax.validation.Valid; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; import io.dropwizard.Configuration; import io.dropwizard.db.DataSourceFactory; public class DropwizardBlogConfiguration extends Configuration { private static final String DATABASE = 'database'; @Valid @NotNull private DataSourceFactory dataSourceFactory = new DataSourceFactory(); @JsonProperty(DATABASE) public DataSourceFactory getDataSourceFactory() { return dataSourceFactory; } @JsonProperty(DATABASE) public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; } }

Le fichier de configuration YAML ressemblerait à ceci:

database: driverClass: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost/dropwizard_blog user: dropwizard_blog password: dropwizard_blog maxWaitForConnection: 1s validationQuery: 'SELECT 1' validationQueryTimeout: 3s minSize: 8 maxSize: 32 checkConnectionWhileIdle: false evictionInterval: 10s minIdleTime: 1 minute checkConnectionOnBorrow: true

La classe ci-dessus désérialise du fichier YAML et place les valeurs du fichier YAML dans cet objet.

Définir une classe d'application

Maintenant, nous devons aller créer la classe d'application principale. Cette classe rassemblera tous les packages, prendra l'application et la mettra en marche pour que vous puissiez l'utiliser.

Voici un exemple de classe d'application dans Dropwizard:

import io.dropwizard.Application; import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter; import io.dropwizard.setup.Environment; import javax.sql.DataSource; import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import org.skife.jdbi.v2.DBI; import com.toptal.blog.auth.DropwizardBlogAuthenticator; import com.toptal.blog.auth.DropwizardBlogAuthorizer; import com.toptal.blog.auth.User; import com.toptal.blog.config.DropwizardBlogConfiguration; import com.toptal.blog.health.DropwizardBlogApplicationHealthCheck; import com.toptal.blog.resource.PartsResource; import com.toptal.blog.service.PartsService; public class DropwizardBlogApplication extends Application { private static final String SQL = 'sql'; private static final String DROPWIZARD_BLOG_SERVICE = 'Dropwizard blog service'; private static final String BEARER = 'Bearer'; public static void main(String[] args) throws Exception { new DropwizardBlogApplication().run(args); } @Override public void run(DropwizardBlogConfiguration configuration, Environment environment) { // Datasource configuration final DataSource dataSource = configuration.getDataSourceFactory().build(environment.metrics(), SQL); DBI dbi = new DBI(dataSource); // Register Health Check DropwizardBlogApplicationHealthCheck healthCheck = new DropwizardBlogApplicationHealthCheck(dbi.onDemand(PartsService.class)); environment.healthChecks().register(DROPWIZARD_BLOG_SERVICE, healthCheck); // Register OAuth authentication environment.jersey() .register(new AuthDynamicFeature(new OAuthCredentialAuthFilter.Builder() .setAuthenticator(new DropwizardBlogAuthenticator()) .setAuthorizer(new DropwizardBlogAuthorizer()).setPrefix(BEARER).buildAuthFilter())); environment.jersey().register(RolesAllowedDynamicFeature.class); // Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class))); } }

Ce que nous avons fait plus tôt est de remplacer la méthode d'exécution de Dropwizard. Dans cette méthode, nous instancions une connexion à partir de DB (Base de données), l'enregistrement de notre vérification de l'état personnalisée (nous en parlerons plus tard), l'initialisation de l'authentification OAuth pour notre service et enfin l'enregistrement d'une ressource Dropwizard.

lequel de ces éléments est utilisé comme mesure du montant total des flux de trésorerie disponibles d'un projet ?

Tout cela sera expliqué plus tard.

Définir une classe de représentation

Maintenant, nous devons commencer à réfléchir à notre API REST et à quelle sera la représentation de notre ressource. Nous devons concevoir le format JSON et la classe de rendu correspondante qui est convertie au format JSON souhaité.

Regardons l'exemple de format JSON pour cet exemple de classe de rendu simple:

{ 'code': 200, 'data': { 'id': 1, 'name': 'Part 1', 'code': 'PART_1_CODE' } }

Pour le format JSON ci-dessus, nous allons créer la classe de rendu comme suit:

import org.hibernate.validator.constraints.Length; import com.fasterxml.jackson.annotation.JsonProperty; public class Representation { private long code; @Length(max = 3) private T data; public Representation() { // Jackson deserialization } public Representation(long code, T data) { this.code = code; this.data = data; } @JsonProperty public long getCode() { return code; } @JsonProperty public T getData() { return data; } }

C'est POJO d'une manière très simple.

Définition d'une classe de ressources

Une ressource est la base des services REST. Ce n'est rien de plus qu'un URI de point final pour accéder à la ressource sur le serveur. Dans cet exemple, nous aurons une classe de ressources avec quelques annotations pour le mappage d'URI de la requête. Puisque Dropwizard utilise l'implémentation JAX-RS, nous définirons le chemin URI à l'aide de l'annotation @Path.

Voici une classe de ressources pour notre exemple Dropwizard:

import java.util.List; import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.jetty.http.HttpStatus; import com.codahale.metrics.annotation.Timed; import com.toptal.blog.model.Part; import com.toptal.blog.representation.Representation; import com.toptal.blog.service.PartsService; @Path('/parts') @Produces(MediaType.APPLICATION_JSON) @RolesAllowed('ADMIN') public class PartsResource { private final PartsService partsService;; public PartsResource(PartsService partsService) { this.partsService = partsService; } @GET @Timed public Representation getParts() { return new Representation(HttpStatus.OK_200, partsService.getParts()); } @GET @Timed @Path('{id}') public Representation getPart(@PathParam('id') final int id) { return new Representation(HttpStatus.OK_200, partsService.getPart(id)); } @POST @Timed public Representation createPart(@NotNull @Valid final Part part) { return new Representation(HttpStatus.OK_200, partsService.createPart(part)); } @PUT @Timed @Path('{id}') public Representation editPart(@NotNull @Valid final Part part, @PathParam('id') final int id) { part.setId(id); return new Representation(HttpStatus.OK_200, partsService.editPart(part)); } @DELETE @Timed @Path('{id}') public Representation deletePart(@PathParam('id') final int id) { return new Representation(HttpStatus.OK_200, partsService.deletePart(id)); } }

Vous pouvez voir comment tout points de terminaison ils sont en fait définis dans cette classe.

Enregistrer une ressource

Je reviendrais maintenant à la classe d'application principale. Vous pouvez voir à la fin de cette classe que nous avons enregistré notre ressource pour être initialisée avec l'exécution du service. Nous devons le faire avec toutes les ressources dont nous pouvons disposer dans notre application. Voici l'extrait de code responsable de cela:

apprendre le c++ à la dure
// Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));

Couche de service

Pour une gestion correcte des exceptions et la possibilité d'être indépendant du moteur de stockage de données, nous allons introduire une classe de service 'de niveau intermédiaire'. C'est la classe que nous appellerons à partir de notre couche de ressources, peu importe ce qui la sous-tend. C'est pourquoi nous avons cette couche particulière entre les couches de ressources et DAO. Voici notre type de service:

import java.util.List; import java.util.Objects; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response.Status; import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException; import org.skife.jdbi.v2.exceptions.UnableToObtainConnectionException; import org.skife.jdbi.v2.sqlobject.CreateSqlObject; import com.toptal.blog.dao.PartsDao; import com.toptal.blog.model.Part; public abstract class PartsService { private static final String PART_NOT_FOUND = 'Part id %s not found.'; private static final String DATABASE_REACH_ERROR = 'Could not reach the MySQL database. The database may be down or there may be network connectivity issues. Details: '; private static final String DATABASE_CONNECTION_ERROR = 'Could not create a connection to the MySQL database. The database configurations are likely incorrect. Details: '; private static final String DATABASE_UNEXPECTED_ERROR = 'Unexpected error occurred while attempting to reach the database. Details: '; private static final String SUCCESS = 'Success...'; private static final String UNEXPECTED_ERROR = 'An unexpected error occurred while deleting part.'; @CreateSqlObject abstract PartsDao partsDao(); public List getParts() { return partsDao().getParts(); } public Part getPart(int id) { Part part = partsDao().getPart(id); if (Objects.isNull(part)) { throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND); } return part; } public Part createPart(Part part) { partsDao().createPart(part); return partsDao().getPart(partsDao().lastInsertId()); } public Part editPart(Part part) { if (Objects.isNull(partsDao().getPart(part.getId()))) { throw new WebApplicationException(String.format(PART_NOT_FOUND, part.getId()), Status.NOT_FOUND); } partsDao().editPart(part); return partsDao().getPart(part.getId()); } public String deletePart(final int id) { int result = partsDao().deletePart(id); switch (result) { case 1: return SUCCESS; case 0: throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND); default: throw new WebApplicationException(UNEXPECTED_ERROR, Status.INTERNAL_SERVER_ERROR); } } public String performHealthCheck() { try { partsDao().getParts(); } catch (UnableToObtainConnectionException ex) { return checkUnableToObtainConnectionException(ex); } catch (UnableToExecuteStatementException ex) { return checkUnableToExecuteStatementException(ex); } catch (Exception ex) { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } return null; } private String checkUnableToObtainConnectionException(UnableToObtainConnectionException ex) { if (ex.getCause() instanceof java.sql.SQLNonTransientConnectionException) { return DATABASE_REACH_ERROR + ex.getCause().getLocalizedMessage(); } else if (ex.getCause() instanceof java.sql.SQLException) { return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage(); } else { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } } private String checkUnableToExecuteStatementException(UnableToExecuteStatementException ex) { if (ex.getCause() instanceof java.sql.SQLSyntaxErrorException) { return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage(); } else { return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage(); } } }

La dernière partie de ceci est en fait une implémentation de bilan de santé, dont nous parlerons plus tard.

Couche DAO, JDBI et Mapper

Dropwizard est compatible avec JDBI et Hibernate. C'est un module Maven séparé, donc ajoutons-le d'abord en tant que dépendance, ainsi que le connecteur MySQL

io.dropwizard dropwizard-jdbi ${dropwizard.version} mysql mysql-connector-java ${mysql.connector.version}

Pour un service CRUD simple, je préfère personnellement JDBI car il est plus simple et beaucoup plus rapide à mettre en œuvre. J'ai créé un schéma MySQL simple avec une table juste à utiliser dans notre exemple. Vous pouvez trouver le script init pour le schéma dans l'origine. JDBI propose une rédaction simple de questions, en utilisant des annotations telles que @SqlQuery pour la lecture et @SqlUpdate pour l'écriture de données. Voici notre interface DAO:

import java.util.List; import org.skife.jdbi.v2.sqlobject.Bind; import org.skife.jdbi.v2.sqlobject.BindBean; import org.skife.jdbi.v2.sqlobject.SqlQuery; import org.skife.jdbi.v2.sqlobject.SqlUpdate; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper; import com.toptal.blog.mapper.PartsMapper; import com.toptal.blog.model.Part; @RegisterMapper(PartsMapper.class) public interface PartsDao { @SqlQuery('select * from parts;') public List getParts(); @SqlQuery('select * from parts where id = :id') public Part getPart(@Bind('id') final int id); @SqlUpdate('insert into parts(name, code) values(:name, :code)') void createPart(@BindBean final Part part); @SqlUpdate('update parts set name = coalesce(:name, name), code = coalesce(:code, code) where id = :id') void editPart(@BindBean final Part part); @SqlUpdate('delete from parts where id = :id') int deletePart(@Bind('id') final int id); @SqlQuery('select last_insert_id();') public int lastInsertId(); }

Comme vous pouvez le voir, c'est assez simple. Cependant, nous devons mapper nos ensembles de résultats SQL à un modèle, ce qui se fait en enregistrant une classe mappeur . Voici notre classe mappeur :

import java.sql.ResultSet; import java.sql.SQLException; import org.skife.jdbi.v2.StatementContext; import org.skife.jdbi.v2.tweak.ResultSetMapper; import com.toptal.blog.model.Part; public class PartsMapper implements ResultSetMapper { private static final String ID = 'id'; private static final String NAME = 'name'; private static final String CODE = 'code'; public Part map(int i, ResultSet resultSet, StatementContext statementContext) throws SQLException { return new Part(resultSet.getInt(ID), resultSet.getString(NAME), resultSet.getString(CODE)); } }

Et notre modèle:

import org.hibernate.validator.constraints.NotEmpty; public class Part { private int id; @NotEmpty private String name; @NotEmpty private String code; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Part() { super(); } public Part(int id, String name, String code) { super(); this.id = id; this.name = name; this.code = code; } }

Vérification de l'état de Dropwizard

Dropwizard offre une prise en charge native de la vérification de l'état. Dans notre cas, nous allons probablement vérifier si la base de données fonctionne avant de dire que notre service est sain. Ce que nous faisons est d'effectuer une action de base de données simple, comme obtenir des parties de la base de données et gérer les résultats potentiels (succès ou exceptions).

principes de conception accent définition

Voici notre implémentation de vérification de l'état dans Dropwizard:

import com.codahale.metrics.health.HealthCheck; import com.toptal.blog.service.PartsService; public class DropwizardBlogApplicationHealthCheck extends HealthCheck { private static final String HEALTHY = 'The Dropwizard blog Service is healthy for read and write'; private static final String UNHEALTHY = 'The Dropwizard blog Service is not healthy. '; private static final String MESSAGE_PLACEHOLDER = '{}'; private final PartsService partsService; public DropwizardBlogApplicationHealthCheck(PartsService partsService) { this.partsService = partsService; } @Override public Result check() throws Exception { String mySqlHealthStatus = partsService.performHealthCheck(); if (mySqlHealthStatus == null) { return Result.healthy(HEALTHY); } else { return Result.unhealthy(UNHEALTHY + MESSAGE_PLACEHOLDER, mySqlHealthStatus); } } }

Ajouter une authentification

Dropwizard prend en charge l'authentification de base et OAuth . Ici, je vais vous montrer comment protéger votre service avec OAuth. Cependant, en raison de la complexité, j'ai omis une structure de base de données sous-jacente et ne montrerai que son évolution. Le déploiement à grande échelle ne devrait pas être un problème à partir d'ici. Dropwizard a deux interfaces importantes que nous devons implémenter.

Le premier est Authenticator . Notre classe doit implémenter la méthode authenticate, qui doit vérifier si l'identifiant d'accès donné est valide. J'appellerais donc cela comme une première porte vers l'application. En cas de succès, cela devrait aboutir à un principal. Ce principal est notre utilisateur réel avec son rôle. Ceci est important pour une autre interface Dropwizard que nous devons implémenter. C'est lui Autorisateur et il est chargé de vérifier si l'utilisateur dispose des autorisations suffisantes pour accéder à une certaine ressource. Donc, si vous revenez en arrière et consultez notre classe de ressources, vous verrez qu'elle nécessite le rôle d'administrateur pour accéder à son points de terminaison . Ces annotations peuvent également se faire par méthode. La prise en charge des autorisations Dropwizard est un module Maven séparé, nous devons donc l'ajouter aux dépendances:

io.dropwizard dropwizard-auth ${dropwizard.version}

Voici les classes de notre exemple qui ne font vraiment rien d'intelligent mais qui sont un squelette pour une autorisation OAuth à grande échelle:

import java.util.Optional; import io.dropwizard.auth.AuthenticationException; import io.dropwizard.auth.Authenticator; public class DropwizardBlogAuthenticator implements Authenticator { @Override public Optional authenticate(String token) throws AuthenticationException { if ('test_token'.equals(token)) { return Optional.of(new User()); } return Optional.empty(); } } import java.util.Objects; import io.dropwizard.auth.Authorizer; public class DropwizardBlogAuthorizer implements Authorizer { @Override public boolean authorize(User principal, String role) { // Allow any logged in user. if (Objects.nonNull(principal)) { return true; } return false; } } import java.security.Principal; public class User implements Principal { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String getName() { return username; } }

Tests unitaires dans DropWizard

Nous allons ajouter quelques tests unitaires à notre application. Je m'en tiens à tester des parties spécifiques du code Dropwizard, dans notre cas: Représentation et Ressource. Nous devrons ajouter les dépendances suivantes à notre fichier Maven:

io.dropwizard dropwizard-testing ${dropwizard.version} org.mockito mockito-core ${mockito.version} test

Pour tester le rendu, nous aurons également besoin d'un exemple de fichier JSON à tester. Créons donc fixtures/part.json sous src/test/resources:

{ 'id': 1, 'name': 'testPartName', 'code': 'testPartCode' }

Et voici la classe de test JUnit:

import static io.dropwizard.testing.FixtureHelpers.fixture; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.toptal.blog.model.Part; import io.dropwizard.jackson.Jackson; public class RepresentationTest { private static final ObjectMapper MAPPER = Jackson.newObjectMapper(); private static final String PART_JSON = 'fixtures/part.json'; private static final String TEST_PART_NAME = 'testPartName'; private static final String TEST_PART_CODE = 'testPartCode'; @Test public void serializesToJSON() throws Exception { final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); final String expected = MAPPER.writeValueAsString(MAPPER.readValue(fixture(PART_JSON), Part.class)); assertThat(MAPPER.writeValueAsString(part)).isEqualTo(expected); } @Test public void deserializesFromJSON() throws Exception { final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getId()).isEqualTo(part.getId()); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getName()) .isEqualTo(part.getName()); assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getCode()) .isEqualTo(part.getCode()); } }

Quand il s'agit de tester des ressources, le point principal des tests de Dropwizard est qu'il se comporte en fait comme un client HTTP, envoyant des requêtes HTTP contre des ressources. Vous ne testez donc pas les méthodes comme vous le feriez normalement dans un cas courant. Voici l'exemple de notre classe PartsResource:

public class PartsResourceTest { private static final String SUCCESS = 'Success...'; private static final String TEST_PART_NAME = 'testPartName'; private static final String TEST_PART_CODE = 'testPartCode'; private static final String PARTS_ENDPOINT = '/parts'; private static final PartsService partsService = mock(PartsService.class); @ClassRule public static final ResourceTestRule resources = ResourceTestRule.builder().addResource(new PartsResource(partsService)).build(); private final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE); @Before public void setup() { when(partsService.getPart(eq(1))).thenReturn(part); List parts = new ArrayList(); parts.add(part); when(partsService.getParts()).thenReturn(parts); when(partsService.createPart(any(Part.class))).thenReturn(part); when(partsService.editPart(any(Part.class))).thenReturn(part); when(partsService.deletePart(eq(1))).thenReturn(SUCCESS); } @After public void tearDown() { reset(partsService); } @Test public void testGetPart() { Part partResponse = resources.target(PARTS_ENDPOINT + '/1').request() .get(TestPartRepresentation.class).getData(); assertThat(partResponse.getId()).isEqualTo(part.getId()); assertThat(partResponse.getName()).isEqualTo(part.getName()); assertThat(partResponse.getCode()).isEqualTo(part.getCode()); verify(partsService).getPart(1); } @Test public void testGetParts() { List parts = resources.target(PARTS_ENDPOINT).request().get(TestPartsRepresentation.class).getData(); assertThat(parts.size()).isEqualTo(1); assertThat(parts.get(0).getId()).isEqualTo(part.getId()); assertThat(parts.get(0).getName()).isEqualTo(part.getName()); assertThat(parts.get(0).getCode()).isEqualTo(part.getCode()); verify(partsService).getParts(); } @Test public void testCreatePart() { Part newPart = resources.target(PARTS_ENDPOINT).request() .post(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class) .getData(); assertNotNull(newPart); assertThat(newPart.getId()).isEqualTo(part.getId()); assertThat(newPart.getName()).isEqualTo(part.getName()); assertThat(newPart.getCode()).isEqualTo(part.getCode()); verify(partsService).createPart(any(Part.class)); } @Test public void testEditPart() { Part editedPart = resources.target(PARTS_ENDPOINT + '/1').request() .put(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class) .getData(); assertNotNull(editedPart); assertThat(editedPart.getId()).isEqualTo(part.getId()); assertThat(editedPart.getName()).isEqualTo(part.getName()); assertThat(editedPart.getCode()).isEqualTo(part.getCode()); verify(partsService).editPart(any(Part.class)); } @Test public void testDeletePart() { assertThat(resources.target(PARTS_ENDPOINT + '/1').request() .delete(TestDeleteRepresentation.class).getData()).isEqualTo(SUCCESS); verify(partsService).deletePart(1); } private static class TestPartRepresentation extends Representation { } private static class TestPartsRepresentation extends Representation { } private static class TestDeleteRepresentation extends Representation { } }

Créez votre application Dropwizard

La meilleure pratique consiste à créer le fichier JAR FAR unique contenant tous les fichiers .class requis pour exécuter l'application. Le même fichier JAR peut être déployé dans un environnement différent du test à la production, sans aucune modification des bibliothèques de dépendances. Pour commencer à créer notre exemple d'application en tant que graisse JAR, nous devons configurer un plugin Maven appelé ombre maven . Vous devez ajouter les entrées suivantes dans la section plugins de votre fichier pom.xml.

Voici l'exemple de configuration Maven pour créer le fichier JAR.

4.0.0 com.endava dropwizard-blog 0.0.1-SNAPSHOT Dropwizard Blog example 1.1.0 2.7.12 6.0.6 1.8 1.8 io.dropwizard dropwizard-core ${dropwizard.version} io.dropwizard dropwizard-jdbi ${dropwizard.version} io.dropwizard dropwizard-auth ${dropwizard.version} io.dropwizard dropwizard-testing ${dropwizard.version} org.mockito mockito-core ${mockito.version} test mysql mysql-connector-java ${mysql.connector.version} org.apache.maven.plugins maven-shade-plugin 2.3 true *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA package shade com.endava.blog.DropwizardBlogApplication

Exécution de l'application

Nous devrions maintenant pouvoir exécuter le service. Si vous avez créé avec succès votre fichier JAR, ce que vous devez faire est d'ouvrir l'invite de commande et d'exécuter la commande suivante pour exécuter votre fichier JAR:

java -jar target/dropwizard-blog-1.0.0.jar server configuration.yml

Si tout s'est bien passé, vous devriez voir quelque chose comme ceci:

INFO [2017-04-23 22:51:14,471] org.eclipse.jetty.util.log: Logging initialized @962ms to org.eclipse.jetty.util.log.Slf4jLog INFO [2017-04-23 22:51:14,537] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: / INFO [2017-04-23 22:51:14,538] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: / INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: / INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: / INFO [2017-04-23 22:51:14,682] io.dropwizard.server.ServerFactory: Starting DropwizardBlogApplication INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8080} INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8081} INFO [2017-04-23 22:51:14,753] org.eclipse.jetty.server.Server: jetty-9.4.2.v20170220 INFO [2017-04-23 22:51:15,153] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources: GET /parts (com.toptal.blog.resource.PartsResource) POST /parts (com.toptal.blog.resource.PartsResource) DELETE /parts/{id} (com.toptal.blog.resource.PartsResource) GET /parts/{id} (com.toptal.blog.resource.PartsResource) PUT /parts/{id} (com.toptal.blog.resource.PartsResource) INFO [2017-04-23 22:51:15,154] org.eclipse.jetty.server.handler.ContextHandler: Started [email protected] {/,null,AVAILABLE} INFO [2017-04-23 22:51:15,158] io.dropwizard.setup.AdminEnvironment: tasks = POST /tasks/log-level (io.dropwizard.servlets.tasks.LogConfigurationTask) POST /tasks/gc (io.dropwizard.servlets.tasks.GarbageCollectionTask) INFO [2017-04-23 22:51:15,162] org.eclipse.jetty.server.handler.ContextHandler: Started [email protected] {/,null,AVAILABLE} INFO [2017-04-23 22:51:15,176] org.eclipse.jetty.server.AbstractConnector: Started [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8080} INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.AbstractConnector: Started [email protected] {HTTP/1.1,[http/1.1]}{0.0.0.0:8081} INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.Server: Started @1670ms

Vous disposez désormais de votre propre application Dropwizard à l'écoute sur les ports 8080 pour les demandes d'application et 8081 pour les demandes d'administration.

Notez que server configuration.yml Il est utilisé pour démarrer le serveur HTTP et transmettre l'emplacement du fichier de configuration YAML au serveur.

Excellent! Enfin, nous avons implémenté un microservice utilisant le cadre Dropwizard. Maintenant, nous allons faire une pause et prendre une tasse de thé. Tu as fait du bon travail.

Accès aux ressources

Vous pouvez utiliser n'importe quel client HTTP comme POSTMAN ou tout autre. Vous devriez pouvoir accéder à votre serveur en appuyant sur http://localhost:8080/parts. Vous devriez recevoir un message indiquant que les informations d'identification sont nécessaires pour accéder au service. Pour vous authentifier, ajoutez l'en-tête Authorization avec la valeur support_test_token. En cas de succès, vous devriez voir quelque chose comme:

{ 'code': 200, 'data': [] }

Ce qui signifie que votre base de données est vide. Créez votre première partie en changeant la méthode HTTP de GET en POST et fournissez cette charge utile:

{ 'name':'My first part', 'code':'code_of_my_first_part' }

Tous les autres points de terminaison Ils fonctionnent de la même manière, alors continuez à jouer et profitez-en.

comment obtenir powerpivot dans excel

Comment changer le chemin du contexte

L'application Dropwizard, par défaut, démarre et s'exécute dans /. Par exemple, si vous ne mentionnez rien sur le chemin du contexte de l'application par défaut, l'application est accessible à partir de l'URL http://localhost: 8080/. Si vous souhaitez configurer votre propre chemin de contexte pour votre application, ajoutez les entrées suivantes à votre fichier YAML. ~~~ serveur: applicationContextPath: / application ~~~

Terminer notre didacticiel Dropwizard

Désormais, lorsque le service REST de Dropwizard est installé, il est temps de résumer certains des principaux avantages ou inconvénients de l'utilisation de Dropwizard, tels que cadre DU REPOS. Il est absolument évident à partir de cet article que Dropwizard propose un amorcer extrêmement rapide de votre projet. Et c'est probablement le plus grand avantage de l'utilisation de Dropwizard.

Il comprendra également toutes les bibliothèques / outils de pointe dont vous aurez besoin pour développer votre service. Vous n'avez donc certainement pas besoin de vous en soucier. Cela vous donne également une très bonne gestion de la configuration. Bien sûr, Dropwizard présente également des inconvénients. En utilisant Dropwizard, vous êtes limité à l'utilisation de ce que Dropwizard propose ou prend en charge. Vous perdez une partie de la liberté à laquelle vous êtes habitué lorsque vous vous développez. Mais même dans ce cas, je n'appellerais même pas cela un inconvénient car c'est exactement ce qui fait de Dropwizard ce qu'il est: facile à configurer, facile à développer, mais toujours un cadre REST très robuste et haute performance.

À mon avis, ajouter de la complexité à la cadre en prenant en charge de plus en plus de bibliothèques tierces, cela introduirait également une complexité inutile dans le développement.

Intégration logicielle optimisée: un didacticiel Apache Camel

Back-End

Intégration logicielle optimisée: un didacticiel Apache Camel
Abandonner les MVP, adopter des prototypes minimum viables (MVPr)

Abandonner les MVP, adopter des prototypes minimum viables (MVPr)

Procédé De Design

Articles Populaires
Ingénieur senior full-stack, équipe post-embauche des talents
Ingénieur senior full-stack, équipe post-embauche des talents
En souvenir de Matthew Osborne
En souvenir de Matthew Osborne
Comment créer un pipeline de déploiement initial efficace
Comment créer un pipeline de déploiement initial efficace
L'impact du Brexit sur le secteur des services financiers
L'impact du Brexit sur le secteur des services financiers
Comment préparer un modèle de tableau des flux de trésorerie qui s'équilibre réellement
Comment préparer un modèle de tableau des flux de trésorerie qui s'équilibre réellement
 
Conquérir la recherche de chaînes avec l'algorithme Aho-Corasick
Conquérir la recherche de chaînes avec l'algorithme Aho-Corasick
Estimation des coûts logiciels dans la gestion de projet agile
Estimation des coûts logiciels dans la gestion de projet agile
5 qualités indispensables des meilleurs chefs de projet
5 qualités indispensables des meilleurs chefs de projet
Comment recréer gratuitement les ressources d'un terminal Bloomberg
Comment recréer gratuitement les ressources d'un terminal Bloomberg
Noyaux d'arbres: quantification de la similitude entre les données structurées en arborescence
Noyaux d'arbres: quantification de la similitude entre les données structurées en arborescence
Articles Populaires
  • que pouvez-vous faire avec node js
  • à quoi servent les rails
  • llc imposé comme c corp
  • générateur de site statique node js
  • élasticité-prix des mesures de la demande__________
Catégories
  • Design De Marque
  • Personnes Et Équipes Produit
  • Innovation
  • Kpi Et Analyses
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt