apeescape2.com
  • メイン
  • 計画と予測
  • モバイル
  • バックエンド
  • ライフスタイル
データサイエンスとデータベース

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

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

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

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



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

イーロンマスク会社に投資する

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

データのクエリにJDBIを使用して、データをMySQLデータベースに保存し、次のエンドポイントを使用します。

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

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

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

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

  • 桟橋: Webアプリケーションを実行するにはHTTPが必要です。 Dropwizardは、Webアプリケーションを実行するためのJettyサーブレットコンテナを埋め込みます。 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アプリケーションを作成するための最初のステップです。 Mavenのpom.xmlに次のエントリを追加してくださいファイル:

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

上記のエントリを追加する前に、dropwizard.versionを追加できます。以下のように:

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のアプリケーションクラスの例を次に示します。

javascriptエラーは関数ではありません
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のrunメソッドをオーバーライドすることです。この方法では、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コネクタとして追加しましょう。

cで何ができますか
io.dropwizard dropwizard-jdbi ${dropwizard.version} mysql mysql-connector-java ${mysql.connector.version}

単純なCRUDサービスの場合、実装がより簡単ではるかに高速であるため、私は個人的にJDBIを好みます。この例で使用するために、1つのテーブルのみを含む単純なMySQLスキーマを作成しました。ソース内にスキーマのinitスクリプトがあります。 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は、ヘルスチェックのネイティブサポートを提供します。私たちの場合、サービスが正常であると言う前に、データベースが稼働しているかどうかを確認したいと思います。実際には、DBからパーツを取得し、潜在的な結果(成功または例外)を処理するなど、いくつかの単純なDBアクションを実行します。

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でサービスを保護する方法を紹介します。ただし、複雑さのために、基礎となるDB構造を省略し、それがどのようにラップされるかを示しました。ここからは、本格的な実装は問題になりません。 Dropwizardには、実装する必要のある2つの重要なインターフェースがあります。

1つ目はAuthenticatorです。私たちのクラスはauthenticateを実装する必要がありますメソッド。指定されたアクセストークンが有効かどうかを確認する必要があります。したがって、これをアプリケーションへの最初のゲートと呼びます。成功した場合は、プリンシパルを返す必要があります。このプリンシパルは、その役割を持つ実際のユーザーです。この役割は、実装する必要のある別のDropwizardインターフェースにとって重要です。これはAuthorizerであり、ユーザーが特定のリソースにアクセスするための十分な権限を持っているかどうかを確認する責任があります。したがって、戻ってリソースクラスを確認すると、エンドポイントにアクセスするために管理者ロールが必要であることがわかります。これらのアノテーションは、メソッドごとにすることもできます。 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ファイルを含む単一のFATJARファイルを作成することです。依存関係ライブラリを変更せずに、同じJARファイルをテストから本番環境まで異なる環境にデプロイできます。サンプルアプリケーションをファットJARとしてビルドするには、maven-shadeというMavenプラグインを構成する必要があります。 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

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

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構成ファイルの場所をサーバーに渡すために使用されます。

優秀な!最後に、Dropwizardフレームワークを使用してマイクロサービスを実装しました。さあ、休憩してお茶を飲みましょう。あなたは本当に良い仕事をしました。

リソースへのアクセス

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

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

DBが空であることを意味します。 HTTPメソッドをGETからPOSTに切り替えて最初の部分を作成し、次のペイロードを指定します。

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

他のすべてのエンドポイントは同じように機能するので、プレイを続けて楽しんでください。

ドキュメントのデザインを計画するときに、デザイナーが知っておく必要があるのは次のうちどれですか?

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

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

server: applicationContextPath: /application

Dropwizardチュートリアルのまとめ

Dropwizard RESTサービスを稼働させたら、DropwizardをRESTフレームワークとして使用することの主な長所と短所をまとめましょう。この投稿から、Dropwizardがプロジェクトの非常に高速なブートストラップを提供していることは明らかです。そして、それがおそらくDropwizardを使用する最大の利点です。

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

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

基本を理解する

Representational State Transfer(REST)の意味は何ですか?

Representational State Transfer(REST)は、ネットワーク化されたリソースがどのように定義され、対処されるかを説明する一連の原則に基づくアーキテクチャのスタイルです(一連の標準と混合しないでください)。

RESTエンドポイントとは何ですか?

RESTエンドポイントは、RESTサービスの公開されたHTTPエントリポイントです。上記の例では、GET / partsが1つのエンドポイントであり、POST / partsが別のエンドポイントです。

REST Webサービスとは何ですか?

REST Webサービスは、RESTアーキテクチャスタイルに従って構造化されたWebアプリケーションです。 HTTPまたはHTTPSポートでリクエストをリッスンし、エンドポイントをクライアントに公開します。

マイクロサービスアーキテクチャとは何ですか?

これは、疎結合サービスのアーキテクチャです。ただし、これらのサービスは一緒になって、システム全体のビジネスロジックを実装します。マイクロサービスの最大の利点は、各小さなサービスが、システムの他の部分に影響を与えることなく、開発、理解、および展開または保守が容易であるという事実にあります。

オープンソースライセンスの開発者ガイド

技術

オープンソースライセンスの開発者ガイド
Django開発者が犯す間違いトップ10

Django開発者が犯す間違いトップ10

技術

人気の投稿
.NETCore-ワイルドでオープンソース化。マイクロソフト、どうしてそんなに時間がかかったの?
.NETCore-ワイルドでオープンソース化。マイクロソフト、どうしてそんなに時間がかかったの?
パーム油への投資家向けガイド
パーム油への投資家向けガイド
B2B UX –一般的な障害と達成可能なソリューション
B2B UX –一般的な障害と達成可能なソリューション
フレーマーチュートリアル:プロトタイプを改善するための7つの簡単なマイクロインタラクション
フレーマーチュートリアル:プロトタイプを改善するための7つの簡単なマイクロインタラクション
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
 
成功する市場開拓戦略を作成する方法
成功する市場開拓戦略を作成する方法
ブロックチェーンID管理:データセキュリティ革命の火付け役
ブロックチェーンID管理:データセキュリティ革命の火付け役
より軽く、より速く-Svelteフレームワークのガイド
より軽く、より速く-Svelteフレームワークのガイド
UXポートフォリオのヒントとベストプラクティス
UXポートフォリオのヒントとベストプラクティス
フリーランサーの個人情報の盗難:それは私に起こりました—これがあなたが知っておくべきことです
フリーランサーの個人情報の盗難:それは私に起こりました—これがあなたが知っておくべきことです
人気の投稿
  • オプション価格の計算方法
  • ccorpまたはscorp llc
  • シミュレーションでは、ユーザーが定義した任意の確率分布を使用できます。
  • node.jsを使用する必要がある理由
  • 不和チャットボットの作り方
  • ビヘイビア駆動開発とは
カテゴリー
ツールとチュートリアル リモートの台頭 データサイエンスとデータベース 設計プロセス 技術 製品の担当者とチーム 革新 エンジニアリング管理 ライフスタイル 人とチーム

© 2021 | 全著作権所有

apeescape2.com