JavaRush /Java Blog /Random-TW /REST API 和另一個測試任務。
Денис
等級 37
Киев

REST API 和另一個測試任務。

在 Random-TW 群組發布
第一部分:開始 從哪裡開始?奇怪的是,但從技術規格來看。確保在閱讀提交的 TOR 後您完全理解其中所寫的內容以及客戶的期望非常重要。首先,這對於進一步實施很重要,其次,如果你沒有實施對你的期望,這對你沒有好處。為了避免浪費空氣,我們來草擬一個簡單的技術規格。因此,我想要一個可以向其發送資料的服務,資料將儲存在該服務上並隨意返回給我。如有必要,我還需要能夠更新和刪除這些資料。幾句話似乎不太清楚吧?我想如何向那裡發送數據?使用什麼技術?該數據將採用什麼格式?也沒有傳入和傳出資料的範例。結論——技術規格已經很糟糕了。讓我們試著重新表達一下:我們需要一個可以處理 HTTP 請求並處理傳輸資料的服務。這將是人事記錄資料庫。我們會有員工,他們按部門和專業劃分,員工可能有任務分配給他們。我們的任務是自動化僱用、解僱、調動員工的會計流程,以及使用 REST API 分配和取消任務的流程。作為第一階段,我們目前僅與員工合作。該服務必須有多個端點才能使用: - 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
  ]
}
第二部分:工作工具 那麼,工作範圍或多或少已經明確了,但是我們該如何做呢?顯然,測試中的此類任務有幾個應用程式目標,看看您如何編碼,迫使您使用 Spring 並稍微使用資料庫。好吧,讓我們這樣做吧。我們需要一個支援 REST API 的 SpringBoot 專案和一個資料庫。在網站 https://start.spring.io/ 上您可以找到您需要的一切。 REST API 或其他測試任務。 - 1 您可以選擇建置系統、語言、SpringBoot 版本、設定工件設定、Java 版本和相依性。點擊「新增依賴項」按鈕將顯示一個帶有搜尋欄的特色選單。單字「rest」和「data」的第一個候選者是「Spring Web」和「Spring Data」——我們將添加它們。Lombok 是一個方便的函式庫,可讓您使用註解透過 getter 和 setter 方法擺脫數公里長的程式碼。透過點擊「生成」按鈕,我們將收到包含項目的存檔,該項目已經可以在我們最喜歡的 IDE 中解壓縮並開啟。預設情況下,我們將收到一個空項目,帶有構建系統的配置文件(在我的例子中它將是gradle,但對於Maven 來說沒有根本區別,還有一個spring 啟動文件)細心的人可以注意兩 REST API 或其他測試任務。 - 2 件事。首先,我有兩個設定檔 application.properties 和 application.yml。預設情況下,您將獲得確切的屬性- 一個可以在其中存儲設置的空文件,但對我來說yml 格式看起來更具可讀性,現在我將顯示一個比較: 儘管事實上左側的圖片看起來更 REST API 或其他測試任務。 - 3 緊湊,很容易看到屬性路徑中有大量重複。右圖是一個常規的yml文件,具有樹形結構,非常容易閱讀。我稍後將在專案中使用該文件。細心的人可能會注意到的第二件事是我的專案已經有幾個包了。目前還沒有健全的程式碼,但值得仔細研究一下。申請書是怎樣寫的?有了具體的任務,我們必須將其分解——將其分解為小的子任務並開始它們的一致實施。對我們有什麼要求?我們需要提供一個客戶可以使用的API;控制器包的內容將負責這部分功能。應用程式的第二部分是資料庫——持久性套件。在其中,我們將儲存諸如資料庫實體(Entities)以及儲存庫之類的東西 - 特殊的 spring 接口,允許您與資料庫互動。服務包將包含服務類別。下面我們就來談談什麼是Spring類型的Service。最後但並非最不重要的一點是 utils 套件。具有各種輔助方法的實用類別將儲存在那裡,例如,用於處理日期和時間的類,或用於處理字串的類,以及誰知道還有什麼。讓我們開始實現該功能的第一部分。 第三部分:控制器
@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 這樣的變量,這些正是我在註釋中寫的,我建議記住這個方法- 它將節省大量擴展或重寫的時間功能 - 將所有內容都包含在配置中總是更方便,而不是對整個專案進行硬編碼。3. @RequiredArgsConstructor是一個Lombok註解,一個方便的函式庫,可以讓你避免寫不必要的東西。在這種情況下,註解相當於該類別將有一個公共建構函數,其中所有欄位都標記為final
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
但是如果註解就夠了,為什麼還要寫這樣的東西呢?:) 順便說一句,恭喜你,這個最私密的final字段無非是臭名昭著的依賴注入。我們接著說,其實employeeService是什麼樣的領域呢?這將是我們專案中處理此端點請求的服務之一。這裡的想法非常簡單。每個類別都應該有自己的任務,並且不應因不必要的操作而超載。如果這是一個控制器,讓它負責接收請求和發送回應,但我們寧願將處理委託給額外的服務。這個類別中剩下的最後一件事是返回我們公司使用上述服務的所有員工清單的唯一方法。該清單本身包裝在一個名為 ResponseEntity 的實體中。我這樣做是為了將來,如果有必要,我可以輕鬆地返回我需要的回應代碼和訊息,自動化系統可以理解這些代碼和訊息。因此,例如 ResponseEntity.ok() 將返回第 200 個代碼,這表示一切都很好,但是如果我返回,例如
return ResponseEntity.badRequest().body(Collections.emptyList());
那麼客戶端將收到代碼 400 - bad reuqest 和回應中的空列表。通常,如果請求不正確,則會傳回此程式碼。但一個控制器不足以讓我們啟動應用程式。我們的依賴項不允許我們這樣做,因為我們仍然必須有一個基礎:) 好吧,讓我們繼續下一部分。 第四部分:簡單的持久性 由於我們的主要任務是啟動應用程序,因此我們現在將僅限於幾個存根。您已經在 Controller 類別中看到,我們傳回 Employee 類型的物件列表,這將是我們的資料庫實體。我們在demo.persistence.entity套件中建立它 ,將來可以用資料庫中的其他實體來補充該實體套件。
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
這是一個像門一樣簡單的類,其註釋準確地說如下:這是一個資料庫實體@Entity,這是一個帶有資料@Data的類別-Lombok。樂於助人的 Lombok 將為我們創建所有必要的 getter、setter、構造函數 - 完整的填充。好吧,錦上添花的是@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")));
}
看起來不太漂亮,是嗎?我們還需要嚴格按照建構函數添加變數的順序。不過,我離題了,讓我們回到正題。現在我們有一個(強制)字段 - 唯一標識符。在本例中,這是一個 Long 類型的數字,在儲存到資料庫時會自動產生。相應地,@Id註解清楚地向我們表明這是一個唯一的識別碼;@GenerateValue負責它的唯一產生。值得注意的是,@Id可以添加到非自動生成的欄位中,但隨後需要手動處理唯一性問題。什麼可以是唯一的員工識別碼?嗯,例如,全名+部門……但是,一個人有全名,他們有可能在同一個部門工作,雖然很小,但確實有——這意味著這個決定是糟糕的。可以添加一堆其他字段,例如僱用日期、城市,但在我看來,所有這些都使邏輯過於複雜。您可能想知道,一堆欄位怎麼可能同時是唯一的?我回答——也許吧。如果你好奇,你可以在谷歌上搜尋@Embeddable和@Embedded之類的東西。好吧,我們已經完成了本質。現在我們需要一個簡單的儲存庫。它看起來像這樣:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
是的,僅此而已。只是一個接口,我們稱之為 EmployeeRepository,它擴展了 JpaRepository,它有兩個類型參數,第一個負責它使用的資料類型,第二個負責鍵類型。在我們的例子中,它們是 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在初始化應用程式時,會找到這個地方並將一個類別物件放入這個變數中。這裡的預設值是 Singleton - 即 所有此類連結都將有一個物件;在設計應用程式時考慮這一點很重要。其實就這樣,應用程式就可以啟動了。不要忘記在配置中輸入必要的設定。 REST API 或其他測試任務。 - 4 我不會描述如何安裝資料庫、建立使用者和資料庫本身,但我只會注意到在 URL 中我使用了兩個附加參數 - 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 的檔案中。但我建議安裝一個成熟的資料庫😉 有關該主題的有用文章:Spring Boot With H2 Database 以防萬一,我將提供完整版本的 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