Spring and Swagger

Pawut Jingjit
3 min readFeb 26, 2023

--

Why & How to implement Swagger 3 on Spring

เพื่อนๆ Backend หลายๆคน โดยเฉพาะในบริษัทใหญ่น่าจะมี Flow การ Develop APIs เช่นนี้

  • เริ่มจากการเขียน APIs Spec ลงที่ไหนซักแห่ง อาจจะเป็น Excel เพื่อเป็น Guide Line ในการพัฒนา อาจรวมไปถึงเป็นเอกสารให้ทีมที่ทำงานร่วมกัน
  • Implement APIs ตามที่วางแผนไว้ในข้อ 1
  • สร้าง Postman Spec เพื่อง่ายแก่การทดสอบ APIs

สังเกตว่า ถ้าเรามีการแก้ไข APIs เพียง 1 เส้น เราจำเป็นต้องแก้ไขถึง 3 ส่วน ไม่นับว่า เราอาจจะเจอปัญหาเกี่ยวกับ Document Version Control อีก (ลองคิดภาพทีมที่ส่ง Postman เป็น File ดู)

TL;NL

  • สำหรับที่เพื่อนๆเข้าใจหลักการแล้ว สามารถข้ามไปอ่านขั้นตอน Implement ได้เลยครับ (ซึ่งจริงๆมันก็แค่ 2 ขั้นตอน)

What Is Swagger?

a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs.

open-source สำหรับการสร้าง OpenAPI Specification ที่สามารถช่วยคุณทั้งการดีไซน์ , สร้าง , ทำเอกสาร , และทดสอบ REST APIS

สร้าง APIs Spec จากไฟล์ .yaml (หรือ JSON) แล้วแปลงเป็น UI ให้ทดสอบ , ทำหน้าที่ทั้ง Document และ Postman ในโปรแกรมเดียวกัน

ด้วยการที่เราจำเป็นต้องเขียน .YAML ให้ได้ตาม Format ของ Swagger นี่จึงอาจไม่ใช่วิธีที่ดีกว่าวิธีเดิมเท่าไหร่

จึงมีผู้พัฒนา Open API generation มา เพื่อ Generate YAML ขึ้นเอง โดยอ้างอิงจาก Code ได้เลย

กล่าวคือ “เราสามารถเขียน Code อย่างเดียว ส่วนของ Document และ Postman สามารถให้ Swagger Generate ให้เองได้”

อนึ่ง Swagger Generate นี้ใช้เฉพาะภาษาที่มี Type ได้เท่านั้นนะ (เพราะภาษาไม่มี Type ตัว Generate ก็ไม่รู้ว่าจะใช้อะไรเป็น Request , Response เช่นกัน)

How to implement Swagger on Spring

เริ่มต้นจาก Spring Initializr เช่นเดิม โดยในที่นี้จะใช้ Gradle (แต่ Maven ก็ Set up ด้วยวิธีใกล้เคียงกัน)

ที่ต้องระวังคือ OpenAPI 3.0 นั้นใช้กับ Java Version หลังๆเท่านั้น (ที่ทดสอบคือหลัง 17) ถ้า Project เพื่อนๆใช้ Version ที่เก่ากว่านั้น ต้องไปใช้ OpenAPI 2.0 แทน ซึ่ง Setting ได้ยากกว่ามาก

Setup Project

Structure ของ Project นี้จะเป็นแบบง่ายๆ ไม่มีการต่อ DB ด้วยซ้ำไป
@Data
public class User {
private String userName;
private String passWord;
}
@Service
public class UserService {

public User createUser(User u){
User user = new User();
user.setUserName(u.getUserName());
user.setPassWord(u.getPassWord());
return user;
}

public List<User> findAll(){
User u1 = new User();
User u2 = new User();
u1.setUserName("user1");
u1.setPassWord("pass1");
u2.setUserName("user2");
u2.setPassWord("pass2");
List<User> users= List.of(u1,u2);

return users;
}
}
@RestController
public class UserController {
@Autowired
UserService userService;

@GetMapping("/users")
public List<User> findAll(){
List<User> result = userService.findAll();
return result;
}

@PostMapping("/users")
public User create(@RequestBody User user){
User result = userService.createUser(user);
return result;
}
}

Impement Swagger On Spring

Gradle

// เพิ่มไปที่ build.gradle (ถ้า IDE ไม่ Auto Download ให้ แนะนำปิดแล้วเปิดใหม่ LOL)
dependencies {
...
implementation 'org.springdoc:springdoc-openapi-ui:1.6.12'
...
}
// เท่านี้ Project เพื่อนๆก็ถือว่าใช้ Swagger แล้ว (จริงๆมันง่ายแค่ Version หลังๆมานะ ถ้าก่อนหน้านี้คือติดตั้งยากมาก)

Maven

 <!--เพิ่มไปที่ pom.yml เช่นกัน-->
<dependency>
...
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.12</version>
...
</dependency>
// เพื่อนๆสามารถ Generate Swagger ได้ผ่าน Annotation @Operation
// summary เพื่อเป็น Desc ว่า API นั้นใช้ทำอะไร
@RestController
public class UserController {
@Autowired
UserService userService;

@Operation(summary = "get user")
@GetMapping("/users")
public List<User> findAll(){
List<User> result = userService.findAll();
return result;
}

@Operation(summary = "create user")
@PostMapping("/users")
public User create(@RequestBody User user){
User result = userService.createUser(user);
return result;
}

}
#จากนั้น เรียกผ่าน URL นี้ ก็น่าจะสามารถใช้งานได้แล้ว
http://localhost:8080/swagger-ui/index.html
เพียง 2 ขั้นตอน ขั้นตอนละ 20 วินาที ก็ได้ Swagger มาใช้งานแล้ว

Warning

// เพื่อนๆหลายคนอาจจะ reuse Object ซักอย่างหนึ่งเช่นนี้
// ซึ่งมันไม่สามารถใช้เป็น Spec ได้ (เพราะ Generator ไม่สามารถรู้ได้เลย ว่าจริงๆมันคือ Object อะไร)
@Data
public class Payload {
private String user;
private Object object;
}
// จะสร้าง Payload สำหรับทุก Object แน่นอนเป็นการกระทำที่ซ้ำซ้อนเกินไป 
// ในที่นี้ แนะนำให้ใช้ Generic Type จะสามารถแก้ปัญหานี้ได้
@Data
public class Payload<T> {
private String user;
private T object;
}

บทส่งท้าย

ช่วงนี้ผู้เขียนบทความได้มีโอกาสได้ทำ Backend แน่นอนว่ามีอะไรที่ไม่ทราบเยอะแยะเลย ก็อาจจะมีบทความเกี่ยวกับ Backend มาบ่อยๆ หวังว่าเพื่อนๆจะไม่เบื่อกันนะครับ

ก่อนหน้านี้ ผู้เขียนเคยใช้ Swagger แบบนั่งเขียน YMAL เอง โดยไม่รู้มาก่อนเลยว่า มัน Auto ได้ แล้วก็คิดว่า หลายๆคนก็ไม่ทราบเหมือนกัน LOL หวังว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆนะครับ

สำหรับเพื่อนที่ Implement ตามแล้วติดอะไรแปลกๆก็ตาม(ซึ่งก็ไม่น่าจะมีนะครับ กับรอบนี้) สามารถดูตัวอย่างได้ที่ Github

สุดท้ายนี้ ก็ขอบคุณเพื่อนๆที่อ่านบทความนี้จนจบ ถ้ามีอะไรผิดพลาดในบทความนี้ หรือมีอะไรอยากแนะนำ สามารถมาคุยกันได้ที่ Page AstralAria นะครับ พบกันในบทความหน้าๆครับ

--

--