JavaRush /จาวาบล็อก /Random-TH /REST API และงานทดสอบอื่น
Денис
ระดับ
Киев

REST API และงานทดสอบอื่น

เผยแพร่ในกลุ่ม
ส่วนที่ 1: การเริ่มต้น จะเริ่มตรงไหน? ผิดปกติพอสมควร แต่จากข้อกำหนดทางเทคนิค สิ่งสำคัญอย่างยิ่งคือต้องแน่ใจว่าหลังจากอ่าน TOR ที่ส่งมาแล้ว คุณจะเข้าใจอย่างถ่องแท้ถึงสิ่งที่เขียนไว้และสิ่งที่ลูกค้าคาดหวัง ประการแรก นี่เป็นสิ่งสำคัญสำหรับการนำไปปฏิบัติต่อไป และประการที่สอง หากคุณไม่ปฏิบัติตามสิ่งที่คาดหวังจากคุณ ก็จะไม่เป็นประโยชน์ต่อคุณ เพื่อหลีกเลี่ยงการสูญเสียอากาศ เรามาร่างข้อกำหนดทางเทคนิคง่ายๆ กัน ดังนั้นฉันต้องการบริการที่สามารถส่งข้อมูลไปได้ โดยจะถูกจัดเก็บไว้ในบริการและส่งคืนตามต้องการ ฉันยังต้องสามารถอัปเดตและลบข้อมูลนี้ได้หากจำเป็น ประโยคสองสามประโยคดูเหมือนจะไม่ชัดเจนใช่ไหม? ฉันต้องการส่งข้อมูลไปที่นั่นได้อย่างไร? ใช้เทคโนโลยีอะไร? ข้อมูลนี้จะอยู่ในรูปแบบใด? นอกจากนี้ยังไม่มีตัวอย่างของข้อมูลขาเข้าและขาออก สรุป-สเปคทางเทคนิคก็แย่ อยู่ แล้ว เรามาลองเรียบเรียงใหม่: เราต้องการบริการที่สามารถประมวลผลคำขอ HTTP และทำงานกับข้อมูลที่ถ่ายโอนได้ นี่จะเป็นฐานข้อมูลบันทึกบุคลากร เราจะมีพนักงาน โดยแบ่งตามแผนกและสาขาเฉพาะทาง พนักงานอาจมีงานมอบหมายให้ทำ งานของเราคือทำให้กระบวนการบัญชีสำหรับพนักงานที่ได้รับการว่าจ้าง ไล่ออก และโอนย้ายโดยอัตโนมัติ รวมถึงกระบวนการมอบหมายและยกเลิกงานโดยใช้ REST API ในระยะที่ 1 ขณะนี้เรากำลังทำงานร่วมกับพนักงานเท่านั้น บริการต้องมีจุดปลายหลายจุดจึงจะใช้งานได้: - POST /พนักงาน - คำขอ 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
  ]
}
Part II: Tools for the job ดังนั้นขอบเขตของงานจะชัดเจนไม่มากก็น้อย แต่เราจะทำอย่างไร? แน่นอนว่างานดังกล่าวในการทดสอบมีเป้าหมายการใช้งานสองสามอย่าง เพื่อดูว่าคุณเขียนโค้ดอย่างไร บังคับให้คุณใช้ Spring และทำงานกับฐานข้อมูลเล็กน้อย เรามาทำสิ่งนี้กันเถอะ เราต้องการโปรเจ็กต์ SpringBoot ที่รองรับ REST API และฐานข้อมูล บนเว็บไซต์ https://start.spring.io/ คุณจะพบทุกสิ่งที่คุณต้องการ REST API หรืองานทดสอบอื่น  - 1 คุณสามารถเลือกระบบบิลด์ ภาษา เวอร์ชัน SpringBoot ตั้งค่าส่วนต่าง ๆ เวอร์ชัน Java และการขึ้นต่อกัน การคลิกปุ่มเพิ่มการพึ่งพาจะเป็นการเปิดเมนูลักษณะเฉพาะพร้อมแถบค้นหา ตัวเลือกแรกสำหรับคำว่าส่วนที่เหลือและข้อมูลคือ Spring Web และ Spring Data - เราจะเพิ่มเข้าไป Lombok เป็นไลบรารี่ที่สะดวกสบายที่ช่วยให้คุณสามารถใช้คำอธิบายประกอบเพื่อกำจัดโค้ดที่มีความยาวหลายกิโลเมตรด้วยวิธี getter และ setter เมื่อคลิกปุ่มสร้าง เราจะได้รับไฟล์เก็บถาวรที่มีโปรเจ็กต์ซึ่งสามารถแตกไฟล์และเปิดใน IDE ที่เราชื่นชอบได้แล้ว ตามค่าเริ่มต้น เราจะได้รับโปรเจ็กต์ว่างพร้อมไฟล์การกำหนดค่าสำหรับระบบบิลด์ (ในกรณีของฉันมันจะเป็นแบบค่อยเป็นค่อยไป แต่สำหรับ Maven นั้นไม่มีความแตกต่างพื้นฐานและมีไฟล์เริ่มต้นสปริงหนึ่งไฟล์) ผู้ที่สนใจสามารถให้ความสนใจกับสอง REST API หรืองานทดสอบอื่น  - 2 สิ่ง . ก่อนอื่นฉันมีไฟล์การตั้งค่าสองไฟล์ application.properties และ application.yml ตามค่าเริ่มต้นคุณจะได้รับคุณสมบัติที่แน่นอน - ไฟล์ว่างที่คุณสามารถจัดเก็บการตั้งค่าได้ แต่สำหรับฉันแล้วรูปแบบ yml ดูอ่านง่ายขึ้นนิดหน่อย ตอนนี้ฉันจะแสดงการเปรียบเทียบ: แม้ว่ารูปภาพด้านซ้ายจะดูกะทัดรัดกว่า REST API หรืองานทดสอบอื่น  - 3 ก็ตาม มันง่ายที่จะเห็นการซ้ำซ้อนจำนวนมากในเส้นทางคุณสมบัติ รูปภาพทางด้านขวาเป็นไฟล์ yml ทั่วไปที่มีโครงสร้างแบบต้นไม้ซึ่งอ่านง่าย ฉันจะใช้ไฟล์นี้ในภายหลังในโครงการ สิ่งที่สองที่ผู้เอาใจใส่อาจสังเกตเห็นคือโปรเจ็กต์ของฉันมีหลายแพ็คเกจอยู่แล้ว ยังไม่มีรหัสที่สมเหตุสมผล แต่ก็คุ้มค่าที่จะผ่านมันไป ใบสมัครเขียนอย่างไร? เมื่อมีงานเฉพาะเจาะจง เราต้องแยกย่อยมัน - แบ่งมันออกเป็นงานย่อยเล็กๆ และเริ่มนำไปปฏิบัติอย่างสม่ำเสมอ สิ่งที่จำเป็นสำหรับเรา? เราจำเป็นต้องจัดเตรียม API ที่ไคลเอนต์สามารถใช้ได้ เนื้อหาของแพ็คเกจคอนโทรลเลอร์จะรับผิดชอบฟังก์ชันการทำงานในส่วนนี้ ส่วนที่สองของแอปพลิเคชันคือฐานข้อมูล - แพ็คเกจการคงอยู่ ในนั้นเราจะจัดเก็บสิ่งต่าง ๆ เช่นเอนทิตีฐานข้อมูล (เอนทิตี) รวมถึงที่เก็บข้อมูล - อินเทอร์เฟซสปริงพิเศษที่ช่วยให้คุณสามารถโต้ตอบกับฐานข้อมูลได้ แพ็คเกจบริการจะมีคลาสบริการ เราจะพูดถึง Service ประเภท Spring ด้านล่างนี้ และสุดท้ายแต่ไม่ท้ายสุด แพ็คเกจ utils คลาสที่เป็นประโยชน์ซึ่งมีวิธีการเสริมทุกประเภทจะถูกเก็บไว้ที่นั่น เช่น คลาสสำหรับการทำงานกับวันที่และเวลา หรือคลาสสำหรับการทำงานกับสตริง และใครจะรู้อะไรอีกบ้าง มาเริ่มใช้งานส่วนแรกของฟังก์ชันกัน ส่วนที่ 3: ผู้ควบคุม
@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 เป็นคำอธิบายประกอบของลอมบอก ซึ่งเป็นไลบรารีที่สะดวกสบายที่ช่วยให้คุณหลีกเลี่ยงการเขียนสิ่งที่ไม่จำเป็น ในกรณีนี้ คำอธิบายประกอบจะเทียบเท่ากับข้อเท็จจริงที่ว่าคลาสจะมีตัวสร้างสาธารณะพร้อมทุกฟิลด์ที่ทำเครื่องหมายว่าเป็นที่สิ้นสุด
public EmployeeController(EmployeeService employeeService) {
    this.employeeService=employeeService;
}
แต่ทำไมเราจะต้องเขียนเรื่องแบบนี้ถ้าคำอธิบายประกอบเพียงอันเดียวก็เพียงพอแล้ว? :) ขอแสดงความยินดีด้วย สนามสุดท้ายที่เป็นส่วนตัวที่สุดนี้ไม่มีอะไรมากไปกว่าการพึ่งพาการฉีดที่โด่งดัง มาดูกันดีกว่าว่า EmployeeService เป็นสาขาประเภทไหน? นี่จะเป็นหนึ่งในบริการในโครงการของเราที่จะประมวลผลคำขอสำหรับตำแหน่งข้อมูลนี้ แนวคิดที่นี่ง่ายมาก แต่ละชั้นเรียนควรมีงานของตัวเองและไม่ควรบรรทุกมากเกินไปด้วยการกระทำที่ไม่จำเป็น หากนี่คือตัวควบคุม ปล่อยให้มันดูแลการรับคำขอและส่งคำตอบ แต่เราอยากจะมอบการประมวลผลให้กับบริการเพิ่มเติม สิ่งสุดท้ายที่เหลืออยู่ในคลาสนี้เป็นวิธีเดียวที่จะส่งคืนรายชื่อพนักงานทั้งหมดของบริษัทของเราที่ใช้บริการที่กล่าวมาข้างต้น รายการนี้รวมอยู่ในเอนทิตีที่เรียกว่า ResponseEntity ฉันทำเช่นนี้เพื่อว่าในอนาคต หากจำเป็น ฉันสามารถส่งคืนรหัสตอบกลับและข้อความที่ฉันต้องการได้อย่างง่ายดาย ซึ่งระบบอัตโนมัติสามารถเข้าใจได้ ตัวอย่างเช่น ResponseEntity.ok() จะส่งคืนโค้ดที่ 200 ซึ่งจะบอกว่าทุกอย่างเรียบร้อยดี แต่ถ้าฉันส่งคืน เป็นต้น
return ResponseEntity.badRequest().body(Collections.emptyList());
จากนั้นลูกค้าจะได้รับรหัส 400 - ข้อผิดพลาดที่ไม่ถูกต้องและรายการว่างในการตอบกลับ โดยทั่วไปแล้วรหัสนี้จะถูกส่งกลับหากคำขอไม่ถูกต้อง แต่ตัวควบคุมเดียวจะไม่เพียงพอสำหรับเราในการเริ่มแอปพลิเคชัน การพึ่งพาของเราจะไม่อนุญาตให้เราทำสิ่งนี้เพราะเรายังต้องมีฐาน :) เรามาดูส่วนต่อไปกันดีกว่า ส่วนที่ 4: ความเพียรที่เรียบง่าย เนื่องจากงานหลักของเราคือการเปิดแอปพลิเคชัน เราจะจำกัดตัวเองอยู่แค่ 2-3 ส่วนในตอนนี้ คุณได้เห็นแล้วว่าในคลาส Controller ที่เราส่งคืนรายการวัตถุประเภท Employee ซึ่งจะเป็นเอนทิตีของเราสำหรับฐานข้อมูล มาสร้างมันขึ้นมาใน แพ็คเกจ demo.persistence.entity กัน ในอนาคต แพคเกจเอนทิตีสามารถเสริมด้วยเอนทิตีอื่น ๆ จากฐานข้อมูลได้
@Entity
@Data
@Accessors(chain = true)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
นี่คือคลาสที่เรียบง่ายเหมือนกับประตู คำอธิบายประกอบที่กล่าวไว้ดังต่อไปนี้: นี่คือเอนทิตีฐานข้อมูล @Entity นี่คือคลาสที่มี data @Data - 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")));
}
ดูไม่สวยมากใช่ไหม? นอกจากนี้เรายังต้องปฏิบัติตามลำดับการเพิ่มตัวแปรตาม Constructor อย่างเคร่งครัด อย่างไรก็ตาม ฉันพูดนอกเรื่อง กลับเข้าประเด็นกันดีกว่า ตอนนี้เรามีหนึ่งฟิลด์ (บังคับ) อยู่ในนั้น - ตัวระบุที่ไม่ซ้ำกัน ในกรณีนี้ นี่คือหมายเลขประเภทยาว ซึ่งสร้างขึ้นโดยอัตโนมัติเมื่อบันทึกลงในฐานข้อมูล ด้วยเหตุนี้ คำอธิบายประกอบ @Id จึงระบุให้เราทราบอย่างชัดเจนว่านี่คือตัวระบุที่ไม่ซ้ำ @GeneratedValue มีหน้าที่รับผิดชอบในการสร้างค่าที่ไม่ซ้ำใคร เป็นที่น่าสังเกตว่าคุณสามารถเพิ่ม @Id ลงในฟิลด์ที่ไม่ได้สร้างขึ้นโดยอัตโนมัติได้ แต่ปัญหาของความเป็นเอกลักษณ์จะต้องได้รับการจัดการด้วยตนเอง ตัวระบุพนักงานที่ไม่ซ้ำกันคืออะไร ตัวอย่างเช่น ชื่อเต็ม + แผนก... อย่างไรก็ตาม บุคคลมีชื่อเต็มและมีโอกาสที่จะทำงานในแผนกเดียวกัน แม้มีขนาดเล็ก แต่มี - นั่นหมายความว่าการตัดสินใจไม่ดี อาจเป็นไปได้ที่จะเพิ่มฟิลด์อื่นๆ มากมาย เช่น วันที่จ้าง เมือง แต่สำหรับฉันทั้งหมดนี้ ดูเหมือนว่าจะทำให้ตรรกะซับซ้อนเกินไป คุณอาจสงสัยว่า เป็นไปได้อย่างไรที่หลายฟิลด์จะไม่ซ้ำกันในคราวเดียว ฉันตอบ - อาจจะ หากคุณสงสัย คุณสามารถ google เกี่ยวกับ @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 ใหม่ - นี่คือสิ่งที่มักจะแสดงถึงเลเยอร์ตรรกะทางธุรกิจ เมื่อรันบริบทสปริง คลาสที่ทำเครื่องหมายด้วยคำอธิบายประกอบนี้จะถูกสร้างขึ้นเป็น Beans เมื่ออยู่ในคลาส EmployeeController เราได้สร้างคุณสมบัติสุดท้าย EmployeeService และแนบ RequiredArgsConstructor (หรือสร้าง Constructor ด้วยมือ) 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:
  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 จากโปรแกรมที่เหมาะสมไปยังปลายทางของเรา ในกรณีนี้เบราว์เซอร์ปกติจะทำได้ แต่ในอนาคตเราจะต้องใช้บุรุษไปรษณีย์ REST API หรืองานทดสอบอื่น  - 6 ใช่ ที่จริงแล้ว เรายังไม่ได้ดำเนินการตามข้อกำหนดทางธุรกิจใดๆ แต่เรามีแอปพลิเคชันที่เริ่มต้นแล้วและสามารถขยายไปสู่ฟังก์ชันที่จำเป็นได้ ดำเนินการต่อ: REST API และการตรวจสอบข้อมูล
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION