JavaRush /Blog Java /Random-VI /API REST và một tác vụ thử nghiệm khác.
Денис
Mức độ
Киев

API REST và một tác vụ thử nghiệm khác.

Xuất bản trong nhóm
Phần I: Bắt đầu Bắt đầu từ đâu? Thật kỳ lạ, nhưng từ các thông số kỹ thuật. Điều cực kỳ quan trọng là đảm bảo rằng sau khi đọc TOR đã gửi, bạn hoàn toàn hiểu những gì được viết trong đó và những gì khách hàng mong đợi. Thứ nhất, điều này rất quan trọng để thực hiện thêm và thứ hai, nếu bạn không thực hiện những gì được mong đợi ở mình, điều đó sẽ không có lợi cho bạn. Để tránh lãng phí không khí, hãy phác thảo một thông số kỹ thuật đơn giản. Vì vậy, tôi muốn một dịch vụ mà tôi có thể gửi dữ liệu đến, dữ liệu sẽ được lưu trữ trên dịch vụ đó và gửi lại cho tôi theo ý muốn. Tôi cũng cần có khả năng cập nhật và xóa dữ liệu này nếu cần thiết . Một vài câu có vẻ không rõ ràng lắm phải không? Tôi muốn gửi dữ liệu tới đó bằng cách nào? Sử dụng những công nghệ nào? Dữ liệu này sẽ ở định dạng nào? Cũng không có ví dụ về dữ liệu đến và đi. Kết luận - đặc điểm kỹ thuật đã tệ rồi . Hãy thử diễn đạt lại: Chúng tôi cần một dịch vụ có thể xử lý các yêu cầu HTTP và hoạt động với dữ liệu được truyền. Đây sẽ là cơ sở dữ liệu hồ sơ nhân sự. Chúng ta sẽ có nhân viên, họ được chia theo các phòng ban, chuyên môn, nhân viên có thể được giao nhiệm vụ. Nhiệm vụ của chúng tôi là tự động hóa quy trình hạch toán nhân viên được thuê, sa thải, thuyên chuyển cũng như quy trình phân công và hủy nhiệm vụ bằng REST API. Là Giai đoạn 1, chúng tôi hiện chỉ làm việc với nhân viên. Dịch vụ phải có một số điểm cuối để hoạt động với nó: - POST /nhân viên - Yêu cầu POST, phải chấp nhận một đối tượng JSON có dữ liệu về nhân viên. Đối tượng này phải được lưu vào cơ sở dữ liệu; nếu đối tượng đó đã tồn tại trong cơ sở dữ liệu thì thông tin trong các trường phải được cập nhật bằng dữ liệu mới. - GET /employee - Yêu cầu GET trả về toàn bộ danh sách nhân viên đã lưu trong cơ sở dữ liệu - DELETE - DELETE /employee để xóa một nhân viên cụ thể Mô hình dữ liệu nhân viên:
{
  "firstName": String,
  "lastName": String,
  "department": String,
  "salary": String
  "hired": String //"yyyy-mm-dd"
  "tasks": [
  	//List of tasks, not needed for Phase 1
  ]
}
Phần II: Công cụ cho công việc Như vậy, phạm vi công việc ít nhiều đã rõ ràng, nhưng chúng ta sẽ thực hiện nó như thế nào? Rõ ràng, các nhiệm vụ như vậy trong bài kiểm tra được đưa ra với một số mục tiêu ứng dụng, để xem cách bạn viết mã, buộc bạn phải sử dụng Spring và làm việc một chút với cơ sở dữ liệu. Vâng, hãy làm điều này. Chúng tôi cần một dự án SpringBoot có hỗ trợ API REST và cơ sở dữ liệu. Trên trang web https://start.spring.io/ bạn có thể tìm thấy mọi thứ bạn cần. API REST hoặc tác vụ thử nghiệm khác.  - 1 Bạn có thể chọn hệ thống xây dựng, ngôn ngữ, phiên bản SpringBoot, đặt cài đặt tạo tác, phiên bản Java và các phần phụ thuộc. Nhấp vào nút Thêm phụ thuộc sẽ hiển thị một menu đặc trưng với thanh tìm kiếm. Các ứng cử viên đầu tiên cho từ còn lại và dữ liệu là Spring Web và Spring Data - chúng ta sẽ thêm chúng vào. Lombok là một thư viện tiện lợi cho phép bạn sử dụng các chú thích để loại bỏ hàng km mã bằng các phương thức getter và setter. Bằng cách nhấp vào nút Tạo, chúng tôi sẽ nhận được một kho lưu trữ có dự án có thể được giải nén và mở trong IDE yêu thích của chúng tôi. Theo mặc định, chúng tôi sẽ nhận được một dự án trống, với tệp cấu hình cho hệ thống xây dựng (trong trường hợp của tôi, nó sẽ là gradle, nhưng với Maven, mọi thứ không có sự khác biệt cơ bản và một tệp khởi động mùa xuân) Những người chú ý có thể chú ý đến hai API REST hoặc tác vụ thử nghiệm khác.  - 2 điều . Đầu tiên, tôi có hai tệp cài đặt application.properties và application.yml. Theo mặc định, bạn sẽ nhận được các thuộc tính chính xác - một tệp trống trong đó bạn có thể lưu trữ cài đặt, nhưng đối với tôi, định dạng yml có vẻ dễ đọc hơn một chút, bây giờ tôi sẽ hiển thị so sánh: Mặc dù thực tế là hình ảnh bên trái trông nhỏ gọn API REST hoặc tác vụ thử nghiệm khác.  - 3 hơn , có thể dễ dàng nhận thấy có một lượng lớn sự trùng lặp trong đường dẫn thuộc tính. Hình bên phải là một tệp yml thông thường có cấu trúc dạng cây khá dễ đọc. Tôi sẽ sử dụng tập tin này sau trong dự án. Điều thứ hai mà những người chú ý có thể nhận thấy là dự án của tôi đã có một số gói. Chưa có mã lành mạnh nào ở đó, nhưng đáng để xem qua chúng. Một ứng dụng được viết như thế nào? Có một nhiệm vụ cụ thể, chúng ta phải phân tách nó - chia nó thành các nhiệm vụ nhỏ và bắt đầu thực hiện nhất quán. Chúng ta được yêu cầu gì? Chúng tôi cần cung cấp một API mà khách hàng có thể sử dụng; nội dung của gói bộ điều khiển sẽ chịu trách nhiệm về phần chức năng này. Phần thứ hai của ứng dụng là cơ sở dữ liệu - gói kiên trì. Trong đó, chúng tôi sẽ lưu trữ những thứ như Thực thể cơ sở dữ liệu (Thực thể) cũng như Kho lưu trữ - giao diện mùa xuân đặc biệt cho phép bạn tương tác với cơ sở dữ liệu. Gói dịch vụ sẽ chứa các lớp dịch vụ. Chúng ta sẽ nói về Spring type Service là gì bên dưới. Và cuối cùng nhưng không kém phần quan trọng là gói utils. Các lớp thực dụng với tất cả các loại phương thức phụ trợ sẽ được lưu trữ ở đó, ví dụ: các lớp để làm việc với ngày và giờ hoặc các lớp để làm việc với chuỗi và ai biết được còn gì nữa. Hãy bắt đầu triển khai phần đầu tiên của chức năng. Phần III: Bộ điều khiển
@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());
    }
}
Bây giờ lớp Nhân viên điều khiển của chúng tôi trông như thế này. Có một số điều quan trọng đáng chú ý ở đây. 1. Các chú thích phía trên lớp, @RestController đầu tiên cho ứng dụng của chúng ta biết rằng lớp này sẽ là điểm cuối. 2. @RequestMapping, mặc dù không bắt buộc, nhưng là một chú thích hữu ích, nó cho phép bạn đặt đường dẫn cụ thể cho điểm cuối. Những thứ kia. để thực hiện nó, bạn sẽ cần gửi yêu cầu không phải tới localhost:port/employee, mà trong trường hợp này là tới localhost:8086/api/v1/employee. Trên thực tế, những api/v1 và nhân viên này đến từ đâu? Từ application.yml của chúng tôi Nếu bạn nhìn kỹ, bạn có thể tìm thấy những dòng sau ở đó:
application:
  endpoint:
    root: api/v1
    employee: employee
    task: task
Như bạn có thể thấy, chúng ta có các biến như application.endpoint.root và application.endpoint.employee, đây chính xác là những gì tôi đã viết trong chú thích, tôi khuyên bạn nên nhớ phương pháp này - nó sẽ tiết kiệm rất nhiều thời gian cho việc mở rộng hoặc viết lại chức năng - sẽ thuận tiện hơn khi có mọi thứ trong cấu hình và không mã hóa toàn bộ dự án. 3. @RequiredArgsConstructor là chú thích Lombok, một thư viện tiện lợi cho phép bạn tránh viết những thứ không cần thiết. Trong trường hợp này, chú thích tương đương với thực tế là lớp sẽ có một hàm tạo công khai với tất cả các trường được đánh dấu là cuối cùng
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
Nhưng tại sao chúng ta phải viết những thứ như vậy nếu chỉ cần một chú thích là đủ? :) Nhân tiện, xin chúc mừng, trường cuối cùng riêng tư nhất này không gì khác hơn là Dependency Insert khét tiếng. Thực ra, chúng ta hãy tiếp tục, nhân viên là loại lĩnh vực nào? Đây sẽ là một trong những dịch vụ trong dự án của chúng tôi sẽ xử lý các yêu cầu cho điểm cuối này. Ý tưởng ở đây rất đơn giản. Mỗi lớp nên có nhiệm vụ riêng và không nên quá tải với những hành động không cần thiết. Nếu đây là bộ điều khiển, hãy để nó đảm nhiệm việc nhận yêu cầu và gửi phản hồi, nhưng chúng tôi muốn giao phó việc xử lý cho một dịch vụ bổ sung. Điều cuối cùng còn lại trong lớp này là phương thức duy nhất trả về danh sách tất cả nhân viên của công ty chúng tôi đang sử dụng dịch vụ nêu trên. Bản thân danh sách được bao bọc trong một thực thể có tên là ResponseEntity. Tôi làm điều này để trong tương lai, nếu cần, tôi có thể dễ dàng trả lại mã phản hồi và tin nhắn tôi cần mà hệ thống tự động có thể hiểu được. Vì vậy, ví dụ: ResponseEntity.ok() sẽ trả về mã thứ 200, nó sẽ nói rằng mọi thứ đều ổn, nhưng nếu tôi quay lại chẳng hạn
return ResponseEntity.badRequest().body(Collections.emptyList());
thì khách hàng sẽ nhận được mã 400 - yêu cầu sai và danh sách trống trong phản hồi. Thông thường mã này được trả về nếu yêu cầu không chính xác. Nhưng một bộ điều khiển sẽ không đủ để chúng ta khởi động ứng dụng. Sự phụ thuộc của chúng tôi sẽ không cho phép chúng tôi làm điều này, bởi vì chúng tôi vẫn phải có cơ sở :) Chà, hãy chuyển sang phần tiếp theo. Phần IV: sự kiên trì đơn giản Vì nhiệm vụ chính của chúng ta là khởi chạy ứng dụng nên bây giờ chúng ta sẽ giới hạn ở một vài sơ khai. Bạn đã thấy trong lớp Controller rằng chúng ta trả về một danh sách các đối tượng thuộc loại Nhân viên, đây sẽ là thực thể của chúng ta cho cơ sở dữ liệu. Hãy tạo nó trong gói demo.persistence.entity . Trong tương lai, gói thực thể có thể được bổ sung với các thực thể khác từ cơ sở dữ liệu.
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
Đây là một lớp đơn giản như một cánh cửa, các Chú thích có nội dung chính xác như sau: đây là một thực thể cơ sở dữ liệu @Entity, đây là một lớp có dữ liệu @Data - Lombok. Lombok hữu ích sẽ tạo cho chúng ta tất cả các getters, setters, constructor cần thiết - nhồi nhét hoàn chỉnh. Chà, một điểm nhấn nhỏ trên chiếc bánh là @Accessors(chain = true) Trên thực tế, đây là một cách triển khai ẩn của mẫu Builder. Giả sử bạn có một lớp với một loạt trường mà bạn muốn gán không phải thông qua hàm tạo mà bằng các phương thức. Theo thứ tự khác nhau, có lẽ không phải tất cả cùng một lúc. Bạn không bao giờ biết loại logic nào sẽ có trong ứng dụng của mình. Chú thích này là chìa khóa của bạn cho nhiệm vụ này. Hãy xem:
public Employee createEmployee() {
    return new Employee().setName("Peter")
        				.setAge("28")
        				.setDepartment("IT");
}
Giả sử rằng chúng ta có tất cả các trường này trong lớp của mình😄Bạn có thể gán chúng, bạn không thể gán chúng, bạn có thể trộn chúng ở nhiều nơi. Trong trường hợp chỉ có 3 thuộc tính, điều này có vẻ không có gì nổi bật. Nhưng có những lớp có số lượng thuộc tính lớn hơn nhiều, chẳng hạn như 50. Và viết một cái gì đó như
public Employee createEmployee() {
    return new Employee("Peter", "28", "IT", "single", "loyal", List.of(new Task("do Something 1"), new Task ("do Something 2")));
}
Trông không đẹp lắm phải không? Chúng ta cũng cần tuân thủ nghiêm ngặt thứ tự thêm biến theo đúng hàm tạo. Tuy nhiên, tôi lạc đề rồi, hãy quay lại vấn đề chính. Bây giờ chúng ta có một trường (bắt buộc) trong đó - một mã định danh duy nhất. Trong trường hợp này, đây là số loại Dài, được tạo tự động khi lưu vào cơ sở dữ liệu. Theo đó, chú thích @Id chỉ rõ cho chúng ta biết rằng đây là mã định danh duy nhất; @GeneratedValue chịu trách nhiệm tạo ra duy nhất của nó. Điều đáng lưu ý là @Id có thể được thêm vào các trường không được tạo tự động, nhưng khi đó vấn đề về tính duy nhất sẽ cần được xử lý theo cách thủ công. Điều gì có thể là mã định danh nhân viên duy nhất? Chà, ví dụ: tên đầy đủ + bộ phận... tuy nhiên, một người có tên đầy đủ, và có khả năng họ sẽ làm việc trong cùng một bộ phận, nhỏ, nhưng có - điều đó có nghĩa là quyết định đó là sai. Có thể thêm một loạt các trường khác, chẳng hạn như ngày thuê, thành phố, nhưng đối với tôi, tất cả những điều này làm logic quá phức tạp. Bạn có thể thắc mắc, làm thế nào mà một loạt trường có thể là duy nhất cùng một lúc? Tôi trả lời - có thể. Nếu tò mò, bạn có thể google về những thứ như @Embeddable và @Embedded Chà, chúng ta đã xong phần cốt lõi. Bây giờ chúng ta cần một kho lưu trữ đơn giản. Nó sẽ trông giống thế này:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
Vâng đó là tất cả. Chỉ là một giao diện, chúng tôi gọi nó là MemberRepository, nó mở rộng JpaRepository có hai tham số được nhập, tham số đầu tiên chịu trách nhiệm về kiểu dữ liệu mà nó hoạt động, tham số thứ hai chịu trách nhiệm về kiểu dữ liệu mà nó hoạt động, tham số thứ hai chịu trách nhiệm về kiểu khóa. Trong trường hợp của chúng tôi, đây là Nhân viên và Long. Bây giờ thế là đủ rồi. Điểm nhấn cuối cùng trước khi khởi chạy ứng dụng sẽ là dịch vụ của chúng tôi:
@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> getAllEmployees() {
        return List.of(new Employee().setId(123L));
    }
}
Đã có NeedArgsConstructor quen thuộc và chú thích @Service mới - đây là thứ thường biểu thị lớp logic nghiệp vụ. Khi chạy bối cảnh mùa xuân, các lớp được đánh dấu bằng chú thích này sẽ được tạo dưới dạng Đậu. Khi trong class StaffController chúng ta đã tạo thuộc tính cuối cùng làEmployService và đính kèm NeedArgsConstructor (hoặc tạo constructor bằng tay) Spring, khi khởi tạo ứng dụng, nó sẽ tìm đến vị trí này và đưa cho chúng ta một đối tượng lớp vào biến này. Mặc định ở đây là Singleton - tức là sẽ có một đối tượng cho tất cả các liên kết như vậy; điều này rất quan trọng cần tính đến khi thiết kế ứng dụng. Trên thực tế, chỉ vậy thôi, ứng dụng có thể được khởi chạy. Đừng quên nhập các cài đặt cần thiết trong config. API REST hoặc tác vụ thử nghiệm khác.  - 4 Tôi sẽ không mô tả cách cài đặt cơ sở dữ liệu, tạo người dùng và chính cơ sở dữ liệu đó, nhưng tôi sẽ chỉ lưu ý rằng trong URL tôi sử dụng hai tham số bổ sung - useUnicore=true và characterEncoding=UTF-8. Điều này được thực hiện để văn bản được hiển thị ít nhiều như nhau trên bất kỳ hệ thống nào. Tuy nhiên, nếu bạn quá lười mày mò cơ sở dữ liệu và thực sự muốn tìm hiểu mã đang hoạt động, thì có một giải pháp nhanh chóng: 1. Thêm phần phụ thuộc sau vào build.gradle:
implementation 'com.h2database:h2:2.1.214'
2. Trong application.yml bạn cần chỉnh sửa một số thuộc tính, tôi sẽ đưa ra một ví dụ đầy đủ về phần lò xo cho đơn giản:
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:
Cơ sở dữ liệu sẽ được lưu trữ trong thư mục dự án, trong một tệp có tên mydb . Nhưng tôi khuyên bạn nên cài đặt một cơ sở dữ liệu chính thức 😉 Bài viết hữu ích về chủ đề: Spring Boot With H2 Database Để đề phòng, tôi sẽ cung cấp phiên bản đầy đủ của build.gradle của mình để loại bỏ sự khác biệt về phần phụ thuộc:
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()
}
Hệ thống đã sẵn sàng khởi chạy: API REST hoặc tác vụ thử nghiệm khác.  - 5 Bạn có thể kiểm tra bằng cách gửi yêu cầu GET từ bất kỳ chương trình phù hợp nào tới điểm cuối của chúng tôi. Trong trường hợp cụ thể này, một trình duyệt thông thường sẽ làm được, nhưng trong tương lai chúng ta sẽ cần Postman. API REST hoặc tác vụ thử nghiệm khác.  - 6 Có, trên thực tế, chúng tôi chưa triển khai bất kỳ yêu cầu kinh doanh nào, nhưng chúng tôi đã có một ứng dụng khởi động và có thể mở rộng sang chức năng được yêu cầu. Tiếp theo: Xác thực dữ liệu và API REST
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION