apeescape2.com
  • メイン
  • 革新
  • ブランドデザイン
  • 計画と予測
  • Kpiと分析
データサイエンスとデータベース

マイクロサービス入門:Dropwizardチュートリアル

私たちは皆、マイクロサービスアーキテクチャの人気が高まっているのを目の当たりにしています。マイクロサービスアーキテクチャでは、Dropwizardが非常に重要な場所を占めています。これは、RESTful Webサービス、より具体的にはツールのセットを作成するためのフレームワークであり、 フレームワーク RESTfulWebサービスを作成するため。

これにより、開発者はプロジェクトをより迅速に開始できます。これにより、アプリケーションをパッケージ化して、本番環境に個別のサービスとして簡単にデプロイできるようになります。でプロジェクトを開始する必要がある状況にあったことがある場合 フレームワークSpring たとえば、あなたはおそらくそれがどれほど苦痛であるかを知っています。

イラスト:Dropwizardチュートリアルのマイクロサービスの例



Dropwizardを使用すると、Mavenの依存関係を追加するだけです。

このブログでは、簡単なDropwizardRESTfulサービスを作成するプロセス全体について説明します。完了すると、「パーツ」での基本的なCRUD操作のサービスが提供されます。 「パーツ」が何であるかは実際には重要ではありません。それは何でもかまいませんが、それは私に最初に起こったことでした。

JDBIを使用してデータをMySQLデータベースに保存し、以下を使用します。 エンドポイント :

  • GET /parts -DBからすべてのパーツを取得する
  • GET /part/{id} DBから特定の部分を取得するには
  • POST /parts -新しいパーツを作成する
  • PUT /parts/{id} -既存のパーツを編集する
  • DELETE /parts/{id} -DBからパーツを削除します

OAuthを使用してサービスを認証し、それにいくつかの単体テストを追加します

デフォルトのDropwizardライブラリ

個別のRESTサービスを作成してそれぞれを構成するために必要なすべてのライブラリを含める代わりに、Dropwizardがそれを行います。 Dropwizardにデフォルトで付属しているライブラリのリストは次のとおりです。

  • 桟橋: Webアプリケーションを実行するにはHTTPが必要です。 Dropwizardはコンテナを組み込んでいます サーブレット Webアプリケーションを実行するための桟橋。 Dropwizardは、アプリケーションをアプリケーションサーバーまたはWebサーバーにデプロイする代わりに、Jettyサーバーをスタンドアロンプ​​ロセスとして呼び出すメインメソッドを定義します。今後、DropwizardはJettyでのみアプリケーションを実行することをお勧めします。 Tomcatのような他のWebサービスは公式にはサポートされていません。
  • ジャージー: ジャージーは、市場で最高のRESTAPI実装の1つです。さらに、これはJAX-RS標準仕様に準拠しており、JAX-RS仕様のリファレンス実装です。 Dropwizardは、RESTfulWebアプリケーションを構築するためのデフォルトのフレームワークとしてJerseyを使用します。
  • ジャクソン: Jacksonは、JSON形式を処理するための事実上の標準です。これは、JSON形式に最適なオブジェクトマッピングAPIの1つです。
  • 指標: Dropwizardには、アプリケーションメトリックを公開するための独自のメトリックモジュールがあります。 エンドポイント HTTP。
  • グアバ: 高度に最適化された不変のデータ構造に加えて、Guavaは、 Java開発 。
  • ログバックとSlf4j: これら2つは、登録メカニズムを改善するために使用されます。
  • フリーマーカーと口ひげ: アプリケーションのテンプレートエンジンを選択することは、重要な決定の1つです。選択したテンプレートエンジンは、より優れたスクリプトを作成するために、より柔軟である必要があります。 Dropwizardは、よく知られた人気のあるFreemarkerおよびMustacheテンプレートエンジンを使用して、ユーザーインターフェイスを構築します。

上記のリストとは別に、Joda Time、Liquibase、Apache HTTPクライアント、DropwizardがRESTサービスを作成するために使用するHibernateValidatorなどの他の多くのライブラリがあります。

Maven構成

Dropwizardは公式にサポートしています Maven 。他のビルドツールを使用できる場合でも、ほとんどのガイドとドキュメントはMavenを使用しているため、ここでも使用します。 Mavenに慣れていない場合は、これを参照できます Mavenチュートリアル 。

これは、Dropwizardアプリケーションを作成するための最初のステップです。次のエントリをファイルに追加しますpom.xml Mavenから:

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

前のエントリを追加する前に、dropwizard.versiónを追加できます。次のようになります。

1.1.0

以上です。 Maven構成の記述は完了です。これにより、プロジェクトに必要なすべての依存関係がダウンロードされます。の現在のバージョン Dropwizardは1.1.0です 、このガイドで使用します。

これで、最初の実際のDropwizardアプリケーションの作成に進むことができます。

構成クラスの定義

Dropwizardは設定をファイルに保存します YAML 。ファイルが必要になりますconfiguration.ymlアプリケーションのルートフォルダにあります。このファイルは、アプリケーションの構成クラスのインスタンスに逆シリアル化され、検証されます。アプリケーションの構成ファイルは、Dropwizard構成クラス(io.dropwizard.Configuration)のサブクラスです。

簡単な構成クラスを作成しましょう。

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構成ファイルは次のようになります。

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

上記のクラスはYAMLファイルから逆シリアル化し、YAMLファイルの値をこのオブジェクトに配置します。

アプリケーションクラスを定義する

次に、メインアプリケーションクラスを作成する必要があります。このクラスは、すべてのパッケージを収集し、アプリケーションを取得して、使用できるように起動して実行します。

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))); } }

以前に行ったことは、Dropwizardの実行メソッドをオーバーライドすることです。この方法では、からの接続をインスタンス化します。 DB (データベース)、カスタムヘルスチェックの登録(これについては後で説明します)、サービスのOAuth認証の初期化、最後にDropwizardリソースの登録。

これについては後で説明します。

表現クラスを定義する

ここで、REST APIと、リソースの表現について考え始める必要があります。 JSON形式と、目的のJSON形式に変換される対応するレンダリングクラスを設計する必要があります。

この単純な表現クラスの例のサンプルJSON形式を見てみましょう。

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

上記のJSON形式の場合、次のようにレンダリングクラスを作成します。

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; } }

これは非常に簡単な方法でのPOJOです。

リソースクラスの定義

リソースは、RESTサービスが基づいているものです。それはのURIにすぎません 終点 サーバー上のリソースにアクセスします。この例では、リクエストURIマッピング用のアノテーションがほとんどないリソースクラスがあります。 DropwizardはJAX-RS実装を使用するため、@Pathアノテーションを使用してURIパスを定義します。

ここでは、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)); } }

あなたはすべてがどのように見ることができます エンドポイント それらは実際にはこのクラスで定義されています。

リソースの記録

ここで、メインのアプリケーションクラスに戻ります。そのクラスの最後に、サービスの実行で初期化されるリソースを登録したことがわかります。アプリケーションで持つことができるすべてのリソースを使用してそれを行う必要があります。これは、その原因となるコードスニペットです。

// Register resources environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));

サービスレイヤー

適切な例外処理とデータストレージエンジンからの独立性のために、「中間層」サービスクラスを導入します。これは、基礎となるものに関係なく、リソースレイヤーから呼び出すクラスです。これが、リソースレイヤーとDAOの間にこの特定のレイヤーがある理由です。これは私たちの種類のサービスです:

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(); } } }

これの最後の部分は、実際にはヘルスチェックの実装です。これについては後で説明します。

DAO、JDBI、マッパーレイヤー

DropwizardはJDBIおよびHibernateと互換性があります。これは別個のMavenモジュールであるため、最初に依存関係として追加し、MySQLコネクタも追加します。

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

単純なCRUDサービスの場合、実装がより簡単ではるかに高速であるため、私は個人的にJDBIを好みます。この例で使用するために、テーブルを使用して単純なMySQLスキーマを作成しました。あなたはスクリプトを見つけることができます 初期化 原点内のスキーマの場合。 JDBIは、読み取り用の@SqlQueryやデータの書き込み用の@SqlUpdateなどの注釈を使用して、簡単な質問の書き込みを提供します。 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(); }

ご覧のとおり、非常に簡単です。ただし、SQL結果セットをモデルにマップする必要があります。これはクラスを登録することによって行われます。 マッパー 。これが私たちのクラスです マッパー :

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)); } }

そして私たちのモデル:

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ヘルスチェック

Dropwizardは、ヘルスチェックのネイティブサポートを提供します。私たちの場合、サービスが正常であると言う前に、データベースが機能しているかどうかを確認する予定です。私たちが行うことは、データベースの一部を取得し、潜在的な結果(成功または例外)を処理するなど、いくつかの単純なデータベースアクションを実行することです。

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); } } }

認証の追加

Dropwizardは基本認証と OAuth 。ここでは、OAuthを使用してサービスを保護する方法を紹介します。ただし、複雑さのために、基礎となるデータベース構造を省略し、それがどのように開発されるかのみを示します。ここからは、フルスケールでの展開は問題になりません。 Dropwizardには、実装する必要のある2つの重要なインターフェイスがあります。

最初は オーセンティケーター 。このクラスは、authenticateメソッドを実装する必要があります。このメソッドは、指定されたアクセス識別子が有効かどうかを確認する必要があります。したがって、これをアプリケーションへの最初の扉と呼びます。成功した場合、それはプリンシパルになるはずです。このプリンシパルは、彼の役割を持つ実際のユーザーです。これは、実装する必要のある別のDropwizardインターフェースにとって重要です。これは 承認者 また、ユーザーが特定のリソースにアクセスするための十分な権限を持っているかどうかを確認する責任があります。したがって、戻ってリソースクラスを確認すると、リソースクラスにアクセスするには管理者ロールが必要であることがわかります。 エンドポイント 。これらの注釈は、メソッドによるものにすることもできます。 Dropwizard認証サポートは別個のMavenモジュールであるため、依存関係に追加する必要があります。

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

この例のクラスは、実際にはスマートなことは何もしませんが、大規模なOAuth承認のスケルトンです。

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; } }

DropWizardでのユニットテスト

アプリケーションにいくつかの単体テストを追加します。私はDropwizardコードの特定の部分、この場合は表現とリソースのテストに固執します。 Mavenファイルに次の依存関係を追加する必要があります。

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

レンダリングをテストするには、テストするサンプルJSONファイルも必要です。では、fixtures/part.jsonを作成しましょう。下src/test/resources:

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

そして、これが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()); } }

リソースのテストに関して、Dropwizardのテストの主なポイントは、実際にはHTTPクライアントのように動作し、リソースに対してHTTP要求を送信することです。したがって、一般的な場合のようにメソッドをテストしているわけではありません。これが私たちのクラスの例です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 { } }

Dropwizardアプリを作成する

ベストプラクティスは、アプリケーションの実行に必要なすべての.classファイルを含む単一のFARJARファイルを作成することです。依存関係ライブラリを変更せずに、同じJARファイルをテストごとに異なる環境にデプロイできます。サンプルアプリケーションの構築を開始するには 太い JAR、Mavenプラグインを構成する必要があります maven-shade 。 pom.xmlファイルのプラグインセクションに次のエントリを追加する必要があります。

これは、JARファイルをビルドするためのMaven構成の例です。

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

アプリケーションの実行

これで、サービスを実行できるようになります。 JARファイルを正しく作成した場合は、コマンドプロンプトを開き、次のコマンドを実行してJARファイルを実行する必要があります。

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

すべてがうまくいけば、次のようなものが表示されます。

llc accorpです
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

これで、独自のDropwizardアプリケーションがポート8080でアプリケーション要求をリッスンし、8081で管理要求をリッスンします。

server configuration.ymlに注意してくださいHTTPサーバーを起動し、YAML構成ファイルの場所をサーバーに渡すために使用されます。

優秀な!最後に、を使用してマイクロサービスを実装しました フレームワーク ドロップウィザード。それでは、休憩してお茶を飲みましょう。あなたは良い仕事をしました。

リソースへのアクセス

POSTMANなどのHTTPクライアントを使用できます。 http://localhost:8080/partsを押すとサーバーにアクセスできるはずです。サービスにアクセスするには資格情報が必要であることを示すメッセージが表示されます。認証するには、ヘッダーを追加しますAuthorization値はsupport_test_tokenです。成功すると、次のように表示されます。

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

これは、データベースが空であることを意味します。 HTTPメソッドをGETからPOSTに変更して最初の部分を作成し、次のペイロードを指定します。

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

他のみんな エンドポイント それらは同じように機能するので、遊び続けて楽しんでください。

コンテキストパスを変更する方法

Dropwizardアプリケーションは、デフォルトで/で起動および実行されます。たとえば、デフォルトでアプリケーションコンテキストパスについて何も言及していない場合、アプリケーションにはURL http://localhost: 8080/からアクセスできます。アプリケーションに独自のコンテキストパスを設定する場合は、YAMLファイルに次のエントリを追加します。 ~~~サーバー:applicationContextPath:/ application ~~~

Dropwizardチュートリアルの終了

Dropwizard RESTサービスをインストールしたら、次のようにDropwizardを使用することの主な長所または短所のいくつかを要約します。 フレームワーク 残り。 Dropwizardが提供していることはこの投稿から絶対に明らかです ブートストラップ プロジェクトが非常に高速です。そして、それはおそらくDropwizardを使用する最大の利点です。

また、サービスの開発に必要なすべての最先端のライブラリ/ツールも含まれます。だからあなたは間違いなくそれについて心配する必要はありません。また、非常に優れた構成管理も提供します。もちろん、Dropwizardにはいくつかの欠点もあります。 Dropwizardを使用すると、Dropwizardが提供またはサポートするものの使用に制限されます。あなたはあなたが成長するときにあなたが慣れているかもしれない自由のいくらかを失います。しかし、それでも私はそれを不利とは言いません。これがDropwizardの特徴であり、構成が簡単で、開発が簡単でありながら、非常に堅牢で高性能なRESTフレームワークです。

私の意見では、 フレームワーク ますます多くのサードパーティライブラリをサポートすることにより、開発に不必要な複雑さももたらします。

UXの神話–プロトタイピング、ユーザーテスト、およびUXの成果物

Uxデザイン

UXの神話–プロトタイピング、ユーザーテスト、およびUXの成果物
OracleからSQLServerおよびSQLServerからOracleへの移行ガイド

OracleからSQLServerおよびSQLServerからOracleへの移行ガイド

データサイエンスとデータベース

人気の投稿
マークダウンを学ぶ:ソフトウェア開発者のためのライティングツール
マークダウンを学ぶ:ソフトウェア開発者のためのライティングツール
美徳シグナリングの芸術:なぜこれほど多くのブランドがそれを間違えるのか
美徳シグナリングの芸術:なぜこれほど多くのブランドがそれを間違えるのか
価格弾力性2.0:理論から現実世界へ
価格弾力性2.0:理論から現実世界へ
ApeeScapeとFacebook-グローバル仮想オフィスの作成
ApeeScapeとFacebook-グローバル仮想オフィスの作成
サイバーセキュリティ:すべてのCEOとCFOが知っておくべきこと
サイバーセキュリティ:すべてのCEOとCFOが知っておくべきこと
 
WebRTCアプリケーションを構築する1年:スタートアップエンジニアリングの教訓
WebRTCアプリケーションを構築する1年:スタートアップエンジニアリングの教訓
PHP 7の概要:新機能と廃止点
PHP 7の概要:新機能と廃止点
ラベルなしデータを使用した半教師あり画像分類
ラベルなしデータを使用した半教師あり画像分類
GWTがブラウザの拡張現実を解き放つ方法
GWTがブラウザの拡張現実を解き放つ方法
UXスケッチについて知っておくべきことすべて
UXスケッチについて知っておくべきことすべて
人気の投稿
  • llc分類cまたはs
  • 不和ボットc ++の作り方
  • 近接性の原理は何ですか
  • mysqlデータベース設計のベストプラクティス
  • C ++プログラミング言語
  • 請負業者とフルタイムの給与
カテゴリー
エンジニアリング管理 革新 ブランドデザイン アジャイル ツールとチュートリアル プロジェクト管理 Uxデザイン デザイナーライフ 仕事の未来 ヒントとツール

© 2021 | 全著作権所有

apeescape2.com