Me kõik oleme tunnistajaks mikroteenuste arhitektuuride populaarsuse suurenemisele. Mikroteenuste arhitektuuris on Dropwizard väga oluline koht. See on raamistik RESTful veebiteenuste või täpsemalt öeldes tööriistade ja raamistikud RESTful veebiteenuste loomiseks.
See võimaldab arendajatel projekti kiiremini käivitada. See aitab teil oma rakendusi pakendada nii, et neid saaks hõlpsasti tootmiskeskkonnas eraldiseisvate teenustena juurutada. Kui teil on kunagi olnud olukorda, kus peate projektis alustama raamistiku kevad Näiteks teate ilmselt, kui valus see võib olla.
kuidas saada discord bot
Dropwizardi puhul on küsimus vaid Maveni sõltuvuse lisamises.
Selles blogis tutvustan teid kogu Dropwizardi RESTful teenuse kirjutamise protsessi. Kui oleme lõpetanud, on meil teenus CRUD-i põhitoimingute jaoks 'osades'. Pole tegelikult vahet, mis on “osa”; See võib olla ükskõik mis, aga see tuli mulle esimesena pähe.
Salvestame andmed MySQL-i andmebaasi, kasutades selleks tutvumiseks JDBI-d ja kasutame järgmist lõpp-punktid :
GET /parts
- kõigi osade hankimiseks DB-stGET /part/{id}
konkreetse osa saamiseks DB-stPOST /parts
- uue osa loomiseksPUT /parts/{id}
- olemasoleva osa redigeerimiseksDELETE /parts/{id}
-osa kustutada andmebaasistKasutame teenuse autentimiseks OAuthi ja lisame sellele seejärel mõned üksuste testid
Selle asemel, et lisada kõik teegid, mis on vajalikud eraldi REST-teenuse loomiseks ja nende konfigureerimiseks, teeb Dropwizard seda meie eest. Siin on Dropwizardi vaikimisi varustatud teekide loend:
Peale ülaltoodud loendi on Dropwizardi poolt REST-teenuste loomiseks palju muid teeke, nagu Joda Time, Liquibase, Apache HTTP Client ja Hibernate Validator.
Dropwizard toetab ametlikult Maven . Isegi kui saate kasutada muid ehitustööriistu, kasutab enamik juhendeid ja dokumentatsiooni Mavenit, nii et me kasutame seda ka siin. Kui te pole Maveniga tuttav, võite sellele viidata Maven õpetus .
See on teie Dropwizardi rakenduse loomise esimene samm. Lisage faili järgmine kirje pom.xml
Mavenist:
io.dropwizard dropwizard-core ${dropwizard.version}
Enne eelmise kirje lisamist võite lisada dropwizard.versión
nagu see on näidatud järgmises:
1.1.0
See on kõik. Olete lõpetanud Maveni konfiguratsiooni kirjutamise. See laadib alla kõik teie projekti jaoks vajalikud sõltuvused. Teenuse praegune versioon Dropwizard on 1.1.0 , nii et kasutame seda selles juhendis.
Nüüd saame edasi liikuda oma esimese päris Dropwizardi rakenduse kirjutamise juurde.
Dropwizard salvestab seaded failidesse YAML . Te vajate faili configuration.yml
rakenduse juurkaustas. See fail deserialiseeritakse teie rakenduse konfiguratsiooniklassi eksemplariks ja valideeritakse. Teie rakenduse konfiguratsioonifail on Dropwizardi seadistusklassi (io.dropwizard.Configuration
) alaklass.
Loome lihtsa seadistusklassi:
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; } }
YAML-i konfiguratsioonifail näeks välja selline:
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
Ülaltoodud klass deserialiseerub YAML-failist ja paneb YAML-faili väärtused sellesse objekti.
Nüüd peame minema ja looma peamise rakendusklassi. See klass kogub kõik paketid kokku, võtab rakenduse ja paneb selle teie kasutusse.
Siin on näide Dropwizardi rakendusklassi kohta:
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))); } }
See, mida me varem tegime, on Dropwizardi käivitamismeetodi alistamine. Selle meetodi abil ühendame ühenduse DB (Andmebaas), meie kohandatud tervisekontrolli registreerimine (sellest räägime hiljem), meie teenuse OAuthi autentimise lähtestamine ja lõpuks Dropwizardi ressursi registreerimine.
Seda kõike selgitatakse hiljem.
Nüüd peame hakkama mõtlema oma REST API-le ja sellele, mis on meie ressursi esitusviis. Peame kujundama JSON-vormingu ja vastava renderdusklassi, mis teisendatakse soovitud JSON-vorminguks.
Vaatame selle lihtsa renderdamisklassi näite JSON-vormingu näidist:
{ 'code': 200, 'data': { 'id': 1, 'name': 'Part 1', 'code': 'PART_1_CODE' } }
Ülaltoodud JSON-vormingu jaoks loome renderdusklassi järgmiselt:
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; } }
See on POJO väga lihtsal viisil.
Ressurss on see, millel REST-teenused põhinevad. See pole midagi muud kui URI lõpp-punkt serveris olevale ressursile juurdepääsemiseks. Selles näites on meil ressursiklassi, kus on vähe märkmeid päringu URI kaardistamiseks. Kuna Dropwizard kasutab JAX-RS juurutamist, määratleme URI tee märkuse @Path
abil.
Siin on meie Dropwizardi näite ressursside klass:
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)); } }
Näete, kuidas kõik lõpp-punktid need on tegelikult selles klassis määratletud.
Naasen nüüd põhirakendusklassi. Selle klassi lõpus näete, et oleme registreerinud oma ressursi, mis lähtestatakse teenuse täitmisega. Peame seda tegema kõigi ressurssidega, mis meie rakenduses võivad olla. Selle eest vastutab koodilõik:
// Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));
Erandite nõuetekohaseks käitlemiseks ja andmesalvestusmootorist sõltumatuse tagamiseks tutvustame 'keskastme' teenindusklassi. See on klass, millele helistame oma ressursikihilt, olenemata sellest, mis selle aluseks on. Sellepärast on meil see konkreetne kiht ressursikihtide ja DAO vahel. See on meie teenus:
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(); } } }
Selle viimane osa on tegelikult tervisekontrolli rakendamine, millest me räägime hiljem.
Dropwizard ühildub JDBI ja talveunerežiimiga. See on eraldi Maveni moodul, nii et lisame selle kõigepealt sõltuvusse ja ka MySQL-pistiku
io.dropwizard dropwizard-jdbi ${dropwizard.version} mysql mysql-connector-java ${mysql.connector.version}
Lihtsa CRUD-teenuse jaoks eelistan isiklikult JDBI-d, kuna seda on lihtsam ja palju kiiremini rakendada. Olen loonud lihtsa MySQL-i skeemi koos tabeliga, et seda lihtsalt meie näites kasutada. Skripti leiate selles päritolus oleva skeemi jaoks. JDBI pakub lihtsat küsimuste kirjutamist, kasutades märkuste jaoks näiteks @SqlQuery lugemiseks ja @SqlUpdate andmete kirjutamiseks. Siin on meie DAO liides:
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(); }
Nagu näete, on see üsna lihtne. Siiski peame oma SQL-i tulemuste komplektid kaardistama mudeli järgi, mis toimub klassi registreerimisega kaardistaja . Siin on meie klass kaardistaja :
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)); } }
Ja meie mudel:
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; } }
Dropwizard pakub tervisekontrolliks kohalikku tuge. Meie juhul kontrollime tõenäoliselt andmebaasi toimimist, enne kui ütleme, et meie teenus on tervislik. Mida me teeme, on mõned lihtsad andmebaasitoimingud, näiteks andmebaasi osade hankimine ja võimalike tulemuste (õnnestumised või erandid) käsitlemine.
Siin on meie tervisekontrolli rakendamine Dropwizardis:
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); } } }
Dropwizard toetab põhilist autentimist ja OAuth . Siin ma näitan teile, kuidas kaitsta teie teenust OAuthi abil. Keerukuse tõttu olen aga andmebaasi aluseks oleva struktuuri välja jätnud ja näitan ainult selle arengut. Täismahus kasutuselevõtt ei tohiks siit probleem olla. Dropwizardil on kaks olulist liidest, mida peame rakendama.
Esimene on Autentija . Meie klass peab kasutama meetodit authenticate
, mis peab kontrollima, kas antud juurdepääsuidentifikaator on kehtiv. Nii et ma nimetaksin seda rakenduse esimese uksena. Kui see õnnestub, peaks selle tulemuseks olema printsipaal. See printsipaal on oma rolliga meie tegelik kasutaja. See on oluline teise Dropwizardi liidese jaoks, mille peame rakendama. See on Volitaja ja see vastutab selle eest, et kontrollida, kas kasutajal on teatud ressursile juurdepääsemiseks piisavalt õigusi. Nii et kui lähete tagasi ja uurite meie ressursiklassi, näete, et selle juurde pääsemiseks on vaja administraatori rolli lõpp-punktid . Need märkused võivad olla ka meetodi järgi. Dropwizardi autoriseerimistugi on eraldi Maveni moodul, seega peame selle lisama sõltuvustesse:
io.dropwizard dropwizard-auth ${dropwizard.version}
Siin on meie näite klassid, mis tegelikult ei tee midagi nutikat, kuid on suuremahulise OAuthi volituse skelett:
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; } }
Lisame oma rakendusele mõned ühikutestid. Jään Dropwizardi koodi konkreetsete osade testimisele, meie puhul: Esindus ja ressurss. Peame oma Maveni faili lisama järgmised sõltuvused:
mittetulunduslikud jätkuva hooldusega pensionäride kogukonnad
io.dropwizard dropwizard-testing ${dropwizard.version} org.mockito mockito-core ${mockito.version} test
Renderduse testimiseks vajame testimiseks ka JSON-faili näidist. Nii et loome fixtures/part.json
all src/test/resources
:
{ 'id': 1, 'name': 'testPartName', 'code': 'testPartCode' }
Ja siin on JUniti testiklass:
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()); } }
Ressursside testimise osas on Dropwizardi testimise põhipunkt see, et see tegelikult käitub nagu HTTP klient, saates ressursside vastu HTTP taotlusi. Nii et te ei testi meetodeid nagu tavaliselt tavalisel juhul. Siin on meie klassi näide 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 { } }
Parim tava on luua üks fail FAR JAR, mis sisaldab kõiki rakenduse käitamiseks vajalikke .class-faile. Sama JAR-faili saab testimisest kuni tootmiseni juurutada erinevas keskkonnas, sõltuvusraamatukogudesse muudatusi tegemata. Et hakata meie näidisrakendust a paks JAR, peame konfigureerima Maveni pistikprogrammi nimega maven-vari . Peate oma pom.xml-faili jaotisse Pluginad lisama järgmised kirjed.
Siin on Maveni konfiguratsiooni näide JAR-faili loomiseks.
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
Nüüd peaksime saama teenust käitada. Kui olete oma JAR-faili edukalt üles ehitanud, peate avama käsurea ja käivitama JAR-faili käivitamiseks järgmise käsu:
java -jar target/dropwizard-blog-1.0.0.jar server configuration.yml
Kui kõik sujus, peaksite nägema midagi sellist:
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
Teil on nüüd oma Dropwizardi rakendus, mis kuulab porte 8080 rakendustaotlusi ja 8081 haldustaotlusi.
Pange tähele, et server configuration.yml
Seda kasutatakse HTTP-serveri käivitamiseks ja YAML-i konfiguratsioonifaili asukoha edastamiseks serverile.
Suurepärane! Lõpuks oleme rakendanud mikroteenuse, kasutades raamistik Dropwizard. Nüüd teeme pausi ja võtame tassi teed. Sa tegid head tööd.
Võite kasutada mis tahes HTTP-klienti, nagu POSTMAN või mõnda muud. Oma serverile peaksite pääsema, vajutades http://localhost:8080/parts
. Peaksite saama teate, mis kinnitab, et teenusele juurdepääsemiseks on vaja mandaati. Autentimiseks lisage päis Authorization
väärtusega support_test_token
. Edu korral peaksite nägema midagi sellist:
{ 'code': 200, 'data': [] }
Mis tähendab, et teie andmebaas on tühi. Looge oma esimene osa, muutes HTTP-meetodi GET-st POST-i ja edastage see kasulik koormus:
{ 'name':'My first part', 'code':'code_of_my_first_part' }
Kõik teised lõpp-punktid Nad töötavad samamoodi, nii et jätkake mängimist ja nautige.
Rakendus Dropwizard käivitub ja töötab vaikimisi /
Näiteks kui te ei maini vaikimisi rakenduse kontekstitee kohta midagi, pääseb rakendusele juurde URL-ist http://localhost: 8080/
. Kui soovite oma rakenduse jaoks oma kontekstitee konfigureerida, lisage oma YAML-faili järgmised kirjed. ~~~ server: applicationContextPath: / rakendus ~~~
Kui olete Dropwizardi REST-teenuse installinud, on aeg kokku võtta Dropwizardi kasutamise mõned peamised eelised või puudused, näiteks raamistik Puhkus. Sellest postitusest on täiesti ilmne, et Dropwizard pakub a bootstrap oma projekti ülikiire. Ja see on ilmselt Dropwizardi kasutamise suurim eelis.
See sisaldab ka kõiki tipptasemel teeke / tööriistu, mida vajate oma teenuse arendamiseks. Nii et te ei pea selle pärast kindlasti muretsema. See annab teile ka väga kena konfiguratsioonihalduse. Muidugi on Dropwizardil ka mõned varjuküljed. Dropwizardi kasutamine on piiratud sellega, mida Dropwizard pakub või toetab. Sa kaotad osa vabadusest, millega võid arenemisel harjuda. Kuid isegi nii ei nimetaks ma seda isegi puuduseks, sest just see muudab Dropwizardi selliseks, nagu see on - lihtne konfigureerida, hõlpsasti arendatav, kuid siiski väga vastupidav ja suure jõudlusega REST-i raamistik.
Minu arvates lisab programmile keerukust raamistik toetades üha uusi kolmandate osapoolte raamatukogusid, tooks see ka arendustöösse tarbetut keerukust.