JavaRush /Java Blog /Random-JA /REST API と別のテスト タスク。
Денис
レベル 37
Киев

REST API と別のテスト タスク。

Random-JA グループに公開済み
パート I: はじめに どこから始めればよいでしょうか? 奇妙なことに、技術仕様によるものです。提出された TOR を読んだ後、そこに何が書かれているか、クライアントが何を期待しているかを完全に理解していることを確認することが非常に重要です。第一に、これはさらなる実装にとって重要であり、第二に、期待されていることを実装しなければ、それはあなたの利益にはなりません。空気の無駄を避けるために、簡単な技術仕様を描いてみましょう。したがって、データを送信できるサービスが必要です。データはサービス上に保存され、自由に返送されます。また、必要に応じてこのデータを更新および削除できる必要もあります。いくつかの文だけでは明確なものではないように思えますよね? そこにデータを送信するにはどうすればよいですか? どのようなテクノロジーを使用すればよいでしょうか? このデータはどのような形式になりますか? また、受信データと送信データの例もありません。結論 - 技術仕様はすでに悪いです。言い換えてみましょう。HTTP リクエストを処理し、転送されたデータを処理できるサービスが必要です。これが人事記録データベースになります。従業員は部門や専門分野ごとに分かれており、従業員にはタスクが割り当てられている場合があります。私たちのタスクは、REST API を使用して、雇用、解雇、異動した従業員の会計処理と、タスクの割り当てとキャンセルのプロセスを自動化することです。フェーズ1として、現在は従業員のみで取り組んでいます。サービスには、それを操作するためのいくつかのエンドポイントが必要です。 - POST /employee - POST リクエスト。従業員に関するデータを含む JSON オブジェクトを受け入れる必要があります。このオブジェクトはデータベースに保存する必要があります。そのようなオブジェクトがデータベースにすでに存在する場合は、フィールド内の情報を新しいデータで更新する必要があります。- GET /employee - データベースに保存されている従業員のリスト全体を返す GET リクエスト - DELETE - 特定の従業員を削除する DELETE /employee 従業員データ モデル:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
パート II: 仕事のためのツール さて、仕事の範囲は多かれ少なかれ明らかですが、どのようにそれを行うのでしょうか? 明らかに、テストにおけるそのようなタスクには、どのようにコーディングするかを確認すること、Spring の使用を強制すること、データベースを少し操作することなど、いくつかのアプリケーション目標が与えられています。さて、これをやってみましょう。REST API サポートとデータベースを備えた SpringBoot プロジェクトが必要です。ウェブサイト https://start.spring.io/ では、必要なものがすべて見つかります。 REST API または別のテスト タスク。 - 1 ビルド システム、言語、SpringBoot バージョンを選択し、アーティファクト設定、Java バージョン、および依存関係を設定できます。「依存関係の追加」ボタンをクリックすると、検索バーのある特性メニューが表示されます。「rest」と「data」という単語の最初の候補は Spring Web と Spring Data です。これらを追加します。Lombok は、アノテーションを使用して、ゲッター メソッドとセッター メソッドで数キロメートルのコードを削除できる便利なライブラリです。「生成」ボタンをクリックすると、プロジェクトが含まれるアーカイブを受け取ります。このアーカイブはすでに解凍されており、お気に入りの IDE で開くことができます。REST API または別のテスト タスク。 - 2 デフォルトでは、ビルド システムの構成ファイルを含む空のプロジェクトを受け取ります (私の場合は Gradle ですが、Maven では基本的な違いはなく、Spring スタートアップ ファイルが 1 つあります)。 注意深い人は2 つの点に注意することができます。 。まず、application.properties と application.yml の 2 つの設定ファイルがあります。デフォルトでは、設定を保存できる空のファイルであるプロパティが正確に取得されますが、私にとっては yml 形式の方がもう少し読みやすいように見えます。ここで比較を示します。 左側の図はよりコンパクトに見えますが REST API または別のテスト タスク。 - 3 、 、プロパティ パスに大量の重複があることが簡単にわかります。右側の図は、非常に読みやすいツリー構造を持つ通常の yml ファイルです。このファイルはプロジェクトの後半で使用します。注意深い人なら気づくかもしれない 2 番目の点は、私のプロジェクトにはすでにいくつかのパッケージがあるということです。まともなコードはまだありませんが、検討してみる価値はあります。申請書はどのように書かれているのでしょうか?特定のタスクがある場合、それを分解する必要があります。それを小さなサブタスクに分割し、それらの一貫した実装を開始します。私たちに何が求められているのでしょうか?クライアントが使用できる API を提供する必要があります。コントローラー パッケージのコンテンツが機能のこの部分を担当します。アプリケーションの 2 番目の部分はデータベース、つまり永続化パッケージです。その中には、データベース エンティティ (エンティティ) やリポジトリ (データベースとの対話を可能にする特別な Spring インターフェイス) などを保存します。サービス パッケージにはサービス クラスが含まれます。Spring 型 Service とは何かについては以下で説明します。そして最後に重要なことは、utils パッケージです。あらゆる種類の補助メソッドを備えた実用的なクラスがそこに保存されます。たとえば、日付と時刻を操作するクラスや文字列を操作するクラスなど、他に何があるかはわかりません。機能の最初の部分の実装を始めましょう。 パート III: コントローラー
@RestController
@RequestMapping("${application.endpoint.root}")
@RequiredArgsConstructor
public class EmployeeController {

    private final EmployeeService employeeService;

    @GetMapping("${application.endpoint.employee}")
    public ResponseEntity<List<Employee>> getEmployees() {
        return ResponseEntity.ok().body(employeeService.getAllEmployees());
    }
}
これで、EmployeeController クラスは次のようになります。ここには注意すべき重要な点がいくつかあります。1. クラス上のアノテーション。最初の @RestController は、このクラスがエンドポイントになることをアプリケーションに伝えます。2. @RequestMapping は必須ではありませんが、エンドポイントの特定のパスを設定できる便利なアノテーションです。それらの。これをノックするには、リクエストを localhost:port/employee ではなく、localhost:8086/api/v1/employee に送信する必要があります。実際、これらの api/v1 と従業員はどこから来たのでしょうか? application.yml から よく見ると、次の行が見つかります。
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
ご覧のとおり、application.endpoint.root や application.endpoint.employee などの変数があります。これらはまさに私が注釈に書いたものです。この方法を覚えておくことをお勧めします。これにより、拡張や書き換えにかかる時間を大幅に節約できます。機能 - プロジェクト全体をハードコーディングするのではなく、すべてを config に含めたほうが常に便利です。3. @RequiredArgsConstructor は Lombok アノテーションで、余計なことを書かなくて済む便利なライブラリです。この場合、アノテーションは、クラスがすべてのフィールドがFinalとしてマークされたパブリック コンストラクターを持つという事実と同等です。
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
しかし、注釈が 1 つあれば十分であるのに、なぜそのようなことを書く必要があるのでしょうか? :) ところで、おめでとうございます。この最もプライベートな最終フィールドは、悪名高い依存性注入に他なりません。それでは、実際のところ、employeeService とはどのようなフィールドでしょうか? これは、このエンドポイントへのリクエストを処理するプロジェクト内のサービスの 1 つになります。ここでの考え方は非常にシンプルです。各クラスには独自のタスクが必要であり、不必要なアクションで過負荷にならないようにしてください。これがコントローラーの場合、リクエストの受信と応答の送信はコントローラーに任せますが、処理は追加のサービスに任せたいと考えます。このクラスに最後に残っているのは、上記のサービスを使用している会社の全従業員のリストを返す唯一のメソッドです。リスト自体は、ResponseEntity というエンティティにラップされています。これは、将来必要になった場合に、自動化システムが理解できる、必要な応答コードとメッセージを簡単に返すことができるようにするためです。したがって、たとえば、ResponseEntity.ok() は 200 番目のコードを返します。これは、すべてが正常であることを示しますが、たとえば、
return ResponseEntity.badRequest().body(Collections.emptyList());
その場合、クライアントはコード 400 (不正な要求) と応答で空のリストを受け取ります。通常、このコードはリクエストが正しくない場合に返されます。ただし、アプリケーションを起動するにはコントローラーが 1 つだけでは不十分です。まだベースが必要なため、依存関係によりこれを行うことはできません :) さて、次の部分に進みましょう。 パート IV: 単純な永続化 主なタスクはアプリケーションを起動することなので、今のところはいくつかのスタブに限定します。Controller クラスで、Employee 型のオブジェクトのリストを返すことはすでに説明しました。これがデータベースのエンティティになります。将来的には、データベースの他のエンティティをエンティティパッケージに追加できるようになります 。
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
これはドアのように単純なクラスで、そのアノテーションには次のことが正確に示されています: これはデータベース エンティティ @Entity、これはデータ @Data - Lombok を持つクラスです。役に立つ Lombok は、必要なすべてのゲッター、セッター、コンストラクター、つまり完全な詰め物を作成します。そうですね、ちょっとしたおまけは@Accessors(chain = true)です 。実際、これは Builder パターンの隠れた実装です。コンストラクターではなくメソッドによって割り当てたい多数のフィールドを持つクラスがあるとします。順序は異なりますが、すべてが同時にではない可能性があります。アプリケーションにどのようなロジックが組み込まれるかはわかりません。この注釈がこのタスクの鍵となります。見てみよう:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
クラスにこれらのフィールドがすべてあると仮定しましょう 😄それらは割り当てることも、割り当てられないことも、場所によっては混在させることもできます。たった 3 つの物件の場合、これは目立ったものではないようです。ただし、それよりもはるかに多数のプロパティ (たとえば 50) を持つクラスもあります。
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
あまりきれいに見えませんね?また、コンストラクターに従って変数を追加する順序に厳密に従う必要があります。話がそれましたが、本題に戻ります。これで、一意の識別子という (必須) フィールドが 1 つ追加されました。この場合、これは Long 型の番号であり、データベースに保存するときに自動的に生成されます。したがって、@Id アノテーションは、これが一意の識別子であることを明確に示しており、@GeneratedValue がその一意の生成を担当します。@Id は自動的に生成されないフィールドに追加できることは注目に値しますが、その場合は一意性の問題に手動で対処する必要があります。一意の従業員識別子は何でしょうか? たとえば、フルネーム + 部署...ただし、ある人にはフルネームがあり、同じ部署で働く可能性があります。小さいながらも存在します。つまり、その決定は間違っています。入社日や都市など、他のフィールドを多数追加することも可能ですが、これらすべてがロジックを複雑にしすぎているように私には思えます。疑問に思うかもしれません。なぜ一度に多数のフィールドを一意にすることができるのでしょうか? 私は答えます - たぶん。興味があれば、@Embeddable や @Embedded などについてグーグルで調べてみてください。これで本質的な部分は終わりました。ここで、単純なリポジトリが必要になります。次のようになります。
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
はい、それだけです。単なるインターフェースであり、これを EmployeeRepository と呼びます。これは 2 つの型指定されたパラメータを持つ JpaRepository を拡張します。1 つ目は動作するデータ型を担当し、2 つ目はキー タイプを担当します。私たちの場合、これらは Employee と Long です。今のところはそれで十分です。アプリケーションを起動する前の最後の仕上げは、サービスです。
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
すでにおなじみの RequiredArgsConstructor と新しい @Service アノテーションがあります。これは通常、ビジネス ロジック層を示します。Spring コンテキストを実行すると、このアノテーションが付けられたクラスが Bean として作成されます。EmployeeController クラスで最終プロパティ EmployeeService を作成し、RequiredArgsConstructor をアタッチした (または手動でコンストラクターを作成した) Spring では、アプリケーションの初期化時にこの場所を見つけて、クラス オブジェクトをこの変数に挿入します。ここでのデフォルトはシングルトンです。このようなすべてのリンクに対して 1 つのオブジェクトが存在します。これは、アプリケーションを設計するときに考慮することが重要です。実際、これだけでアプリケーションを起動できます。構成に必要な設定を入力することを忘れないでください。 REST API または別のテスト タスク。 - 4 データベースのインストール方法、ユーザーとデータベース自体の作成方法については説明しませんが、URL で 2 つの追加パラメータ useUnicore=true とcharacterEncoding=UTF-8 を使用していることに注意してください。これは、テキストがどのシステムでもほぼ同等に表示されるようにするために行われました。ただし、データベースをいじるのが面倒で、動作するコードを実際にいじりたい場合は、簡単な解決策があります。 1. 次の依存関係を build.gradle に追加します。
implementation 'com.h2database:h2:2.1.214'
2. application.yml では、いくつかのプロパティを編集する必要があります。簡単にするために、Spring セクションの完全な例を示します。
spring:
  application:
    name: "employee-management-service"
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.H2Dialect
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:./mydb
    username: sa
    password:
データベースは、プロジェクト フォルダーのmydb というファイルに保存されます。ただし、本格的なデータベースをインストールすることをお勧めします 😉 このトピックに関する役立つ記事: H2 データベースを使用した Spring Boot 念のため、依存関係の不一致を排除するために、build.gradle の完全バージョンを提供します。
plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'mysql:mysql-connector-java:8.0.30'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
システムは起動する準備ができています。 REST API または別のテスト タスク。 - 5 適切なプログラムからエンドポイントに GET リクエストを送信することで確認できます。この特定のケースでは、通常のブラウザで十分ですが、将来的には Postman が必要になります。 REST API または別のテスト タスク。 - 6 はい、実際、まだビジネス要件を実装していませんが、起動して必要な機能まで拡張できるアプリケーションがすでにあります。続き: REST API とデータ検証
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION