February 05, 2026

Backend | Swagger

Swagger 是一個非常流行的工具,可以幫助我們以 Specification First 的方式來開發 RESTful API, Swagger 允許我們使用 YAML 或 JSON 來定義 API 的規格,然後我們可以直接透過 Specification 來產生 Controller 的程式碼, 這樣就能夠讓 API 的定義和實現分離,提升開發效率和 API 的可維護性。

目前 Swagger 已經更名為 OpenAPI,不過很多人還是習慣稱呼 Swagger,所以兩種名稱都可以使用

1. Overview

依照 Specification First 的方式來開發 RESTful,也可以算是 Specification Driven Development (SDD) 的實踐方式

如果我們要開發一個 Backend 的 RESTful API,主要有兩種做法:

  1. Code First:
    • 我們直接到後端框架中撰寫 Controller 的程式碼,然後在程式碼中使用註解的方式來定義 API 的規格, 例如使用 Spring Boot 的 @RestController@RequestMapping 等註解來定義 API 的路徑、HTTP 方法、參數等等
    • 優點是開發速度快,因為我們直接在程式碼中定義 API 的規格,不需要額外的工具來管理 API 的規格
    • 有可能導致 API 設計不一致,Docs 不完整或者導致前後端耦合
  2. Specification First:
    • 我們先使用 Swagger 的 YAML 或 JSON 來定義 API 的規格,然後透過 Swagger 的工具來產生 Controller 的程式碼, 這樣就能夠讓 API 的定義和實現分離,提升開發效率和 API 的可維護性
    • 優點是前後端可以同時進行開發,前後端都根據 API 的規格來進行開發
    • 缺點是需要額外的工具來管理 API 的規格,並且在開發過程中需要保持 API 的規格和實現的一致性

目前如果是大型專案或者 Microservice 的架構,通常會傾向使用 Specification First 的方式來開發 RESTful API, 因為這樣可以讓 API 的定義更加清晰,並且能夠更好地管理 API 的版本和變更。 而在中小型專案或者是快速開發的情況下,可能會傾向使用 Code First 的方式來開發 RESTful API, 因為這樣可以更快地開始開發,不需要額外的工具來管理 API 的規格。

1.1 Swagger Example

我們來設計一個簡單的範例,假如我們有一個 User Registration 的 API,我們可以使用 Swagger 的 YAML 來定義 API 的規格,如下所示:

openapi: 3.0.0
info:
  title: User Registration API
  description: API for user registration
  version: 1.0.0
servers:
  - url: http://example.com/v1
    description: Production server
  - url: http://test.example.com/v1
    description: Test server
paths:
  /users:
    post:
      summary: Register a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                email:
                  type: string
                password:
                  type: string
      responses:
        201:
          description: User created successfully
        400:
          description: Bad request

這是一個很簡單的 OpenAPI 的定義,我們從上往下逐一來解釋一下這個定義的內容:

  1. info: API 的基本資訊,包括 API 的標題、描述和版本號
  2. servers: API 的伺服器資訊,包括伺服器的 URL 和描述
    • 這裡有兩個 Server 一個用於正式環境,一個用於測試環境
    • 通常是給 Client 來選擇要使用哪個 Server 來呼叫 API
  3. paths: API 的路徑和 HTTP 方法
    • /users: 這個路徑的 API 並且只有 POST 方法
    • requestBody: POST 方法的 Body 的內容,必須為 JSON 格式
      • 包含 username, email, password 這三個欄位
  4. responses: API 的回應,包括成功和失敗的回應
    • 成功的回應是 201,表示使用者創建成功
    • 失敗的回應是 400,表示請求錯誤

這樣我們透過 Swagger 可以很清楚的知道 API 的規格為 POST /users,並且知道這個 API 的請求和回應的格式, 這樣就能夠讓前後端都根據這個規格來進行開發。


2. OpenAPI to Spring Boot Controller

實際開發直接將 OpenAPI 的定義轉換成 Spring Boot 的 Controller 程式碼,這樣能確保規格和實現的一致性

OpenAPI Generator 是轉換 OpenAPI 定義成程式碼的工具,支援多種語言和框架,包括 Java 的 Spring

那我們要先了解那些部分是規格可以定義然後直接轉換成 Controller 的程式碼,主要可以分為以下轉換:

  1. paths 的部分可以轉換成 Controller 的路徑和 HTTP 方法
  2. components/schemas 的部分可以轉換成 DTO 的類別定義

DTO 是一種用於在不同層之間傳遞資料的物件,通常用於將資料從 Controller 層傳遞到 Service 層,或者從 Service 層傳遞到 Repository 層, 透過 DTO 我們可以避免直接使用實體類別來傳遞資料,這樣可以避免實體類別的變更對其他層造成影響,並且可以更好地控制資料的格式和內容。

openapi: 3.0.3

info:
  title: User Registration API
  version: 1.0.0

paths:
  /:
    get:
      summary: Root health endpoint
      operationId: getRoot
      responses:
        200:
          description: Hello message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RootResponse'

components:
    schemas:
        RootResponse:
            type: object
            properties:
                message:
                    type: string
  • $ref 是 OpenAPI 中用來引用其他部分定義的語法,透過 $ref 我們可以在 OpenAPI 定義中引用其他部分的定義
    • 也可以引用多個檔案,例如 ./schemas.yaml 中定義了 RootResponse 的 schema
    • 使用 $ref: './schemas.yaml#/RootResponse' 來引用這個 schema
  • 這裡我們指定了 200 的回應內容為 RootResponse 的 schema,這樣在轉換成 Controller 程式碼時, 就會知道這個 API 的回應格式是 RootResponse 這個 DTO 類別

2.1 Spring Boot File Structure

這裡我就不準備現成環境了,可以參考 Spring Boot Swagger Example 這個專案的結構來準備一個 Spring Boot 的專案結構, 不過他的版本有點久,所以自己架設也是很簡單的。

讓我們準備好一個 Spring Boot 的專案結構,如下所示:

.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src ── main ── resources ── openapi ── openapi.yaml
└── target ── generated-sources
  • Swagger 相關的 yaml 通常被放置在 src/main/resources/openapi 這個目錄下
  • 產生後的檔案被放置在 target/generated-sources 這個目錄下

然後去設定 pom.xml 來使用 OpenAPI Generator 的 Maven Plugin,如下:

<build>
    <plugins>
        <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>7.5.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <inputSpec>${project.basedir}/src/main/resources/openapi/openapi.yaml</inputSpec>
                        <generatorName>spring</generatorName>
                        <library>spring-boot</library>
                        <output>${project.build.directory}/generated-sources/openapi</output>
                        <apiPackage>com.example.demo.api</apiPackage>
                        <modelPackage>com.example.demo.model</modelPackage>
                        <configOptions>
                            <interfaceOnly>true</interfaceOnly>
                            <useSpringBoot3>true</useSpringBoot3>
                            <useTags>true</useTags>
                        </configOptions>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

之後就能在 build 的過程中自動產生 Controller 的程式碼,但這裡要注意產生的只有 interface 的程式碼, 因此還是需要自己透過 implementation 的方式來實現 Controller 的邏輯。

./target/generated-sources/openapi/…/RootApi.java:

package com.example.demo.api;

/** ... **/

@Validated
@Tag(name = "root", description = "the root API")
public interface RootApi {
    
    /** ... **/

    @RequestMapping(method = RequestMethod.GET, value = "/", produces = {"application/json"})

    default ResponseEntity<RootResponse> getRoot() {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    String exampleString = "{ \"message\" : \"message\" }";
                    ApiUtil.setExampleResponse(request, "application/json", exampleString);
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }
}

Controller 依然是由開發者透過 implementationOverride 的方式來實現的, 同時我們也有了 DTO 的類別定義,這裡的 RootResponse 就是從 OpenAPI 定義中轉換過來的 DTO 類別。

./src/main/java/…/RootApiController.java:

package com.example.demo.controller;

/** ... **/

@RestController
public class RootApiController implements RootApi {

    @Override
    public ResponseEntity<RootResponse> getRoot() {
        RootResponse response = new RootResponse().message("hello, word");
        return ResponseEntity.ok(response);
    }
}

以上就是我們如何從 OpenAPI 的定義來產生 Spring Boot Controller 的程式碼,這份 OpenAPI 不只能用於產生 Controller 的程式碼, 也能用於產生 Client 的程式碼,或者是用於產生 API 的文件。

注意一個原則,新手常會犯的錯誤是去修改了自動產生的程式碼,應該要把這些自動產生的程式碼當成是可以被覆蓋的, 然後在自己的 Controller 中去實現這些程式碼的邏輯,不管是 Swagger 或者其他工具都是一樣的原則

這樣我們就大致了解了 Swagger 的使用方式,以及如何從 OpenAPI 的定義來產生 Spring Boot Controller 的程式碼, 下次開發新服務時就可以考慮使用 Specification First 的方式來開發 RESTful API。

Last Edit

02-07-2026 18:32

results matching ""

    No results matching ""

    , software, restful api, swagger