iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0
IT 管理

我要成為文件工程師── Web API 文件撰寫系列 第 11

[Day 11] TypeSpec:撰寫 OpenAPI 的領域特定語言 (五)

  • 分享至 

  • xImage
  •  

模型

使用關鍵字 model 建立模型,模型可以在不同的操作或其他模型中重複使用,編譯時模型會輸出在 components 節點,並在各個使用的地方透過 $ref 參考。

1. 宣告模型

使用 model 關鍵字建立一個名為 Member 的模型,並依照各個內容欄位設定欄位名稱、型別及說明。我個人習慣將模型獨立建立一個目錄與對應的命名空間,並一個模型建立一個 TypeSpec 檔案,範例程式碼如下:

// member/model/member-model.tsp

namespace MemberService.Member.Model;

model MemberModel {
    @doc("會員 ID")
    id: int64;

    @doc("會員姓名")
    name: string;

    @doc("信箱")
    email: string;

    @doc("手機號碼")
    phone: string;

    @doc("生日")
    birthday?: utcDateTime;
}

2. 匯入並引用模型的命名空間

若要在 member/member.tsp 中的取得單一會員 API 使用 MemberModel,由於 MemberModel 在不同的檔案及命名空間中,需先 import <file-path> 匯入該檔案。又因 MemberModel 所屬的命名空間為 MemberService.Member.Model,我們可以透過以下任一方式使用 MemberModel

  • 使用完整的命名空間和模型名稱,例如:MemberService.Member.Model.MemberModel
  • 使用和目前命名空間相對的命名空間和模型名稱,例如:目前所屬命名空間為 MemberService.Member,補上差異的命名空間 Model.MemberModel 來指定 MemberModel
  • 在上方引用命名空間後直接使用模型名稱,例如:引用 using MemberService.Model; 後使用 MemberModel

這邊使用最後一種做法來匯入並引用,範例程式碼如下:

// member/member.tsp

import "@typespec/http";
import "./model/member-model.tsp";

using TypeSpec.Http;
using MemberService.Member.Model;

namespace MemberService.Member;

// 以下省略

3. 在回傳中使用模型

接著將原本散在取得單一會員 API 中的回傳欄位移除,改使用 MemberModel 作為 Response Body,範例程式碼如下:

// member/member.tsp

import "@typespec/http";
import "./model/member-model.tsp";

using TypeSpec.Http;
using MemberService.Member.Model;

namespace MemberService.Member;

@summary("取得單一會員")
@doc("""
    這個 API 用來取得單一會員的資料。
    這個 API 需要提供會員 ID,並回應該會員的基本資料。
""")
@get()
@route("members/{memberId}")
op get(@path memberId: int64): {
    @statusCode _: 200;
    @header `x-request-id`: string;
    @body body: MemberModel;
};

4. 在不同地方使用相同模型

將這個模型也使用在註冊會員 API 的 Request Body 和 Response Body,我們先宣告註冊會員 API 的操作 register,並設定其他參數和回傳資訊,範例程式碼如下:

// member/member.tsp

// 以上省略

@summary("註冊會員")
@doc("""
    這個 API 用來註冊新的會員。
    這個 API 需要提供會員 ID、會員姓名、信箱、手機號碼和生日,並回應註冊成功的會員 ID。
""")
@post()
@route("members")
op create(@body body: MemberModel): {
    @statusCode _: 200;
    @header `x-request-id`: string;
    @body body: MemberModel;
};

這時編譯後產生的 openapi.yaml 如下:

openapi: 3.0.0
info:
  title: 會員服務
  version: 1.0.0
tags: []
paths:
  /_hc:
    get:
      operationId: healthCheck
      summary: 確認服務健康狀態
      description: |2-
            這個 API 用來確認服務是否正常運作。
            這個 API 不需要任何參數,只要回應 204 即表示服務正常。
      parameters: []
      responses:
        '204':
          description: 'There is no content to send for this request, but the headers may be useful. '
  /members:
    post:
      operationId: Member_register
      summary: 註冊會員
      description: |2-
            這個 API 用來註冊新的會員。
            這個 API 需要提供會員 ID、會員姓名、信箱、手機號碼和生日,並回應註冊成功的會員 ID。
      parameters: []
      responses:
        '200':
          description: The request has succeeded.
          headers:
            x-request-id:
              required: true
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Member.Model.MemberModel'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Member.Model.MemberModel'
  /members/{memberId}:
    get:
      operationId: Member_get
      summary: 取得單一會員
      description: |2-
            這個 API 用來取得單一會員的資料。
            這個 API 需要提供會員 ID,並回應該會員的基本資料。
      parameters:
        - name: memberId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: The request has succeeded.
          headers:
            x-request-id:
              required: true
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Member.Model.MemberModel'
components:
  schemas:
    Member.Model.MemberModel:
      type: object
      required:
        - id
        - name
        - email
        - phone
      properties:
        id:
          type: integer
          format: int64
          description: 會員 ID
        name:
          type: string
          description: 會員姓名
        email:
          type: string
          description: 信箱
        phone:
          type: string
          description: 手機號碼
        birthday:
          type: string
          format: date-time
          description: 生日
servers:
  - url: https://api.staging.example.com/v1
    description: 測試環境
    variables: {}
  - url: https://api.example.com/v1
    description: 正式環境
    variables: {}

可看到 components.schemas 節點下產出了 Member.Model.MemberModel,並在不同的地方透過 $ref: '#/components/schemas/Member.Model.MemberModel' 使用。

Redoc 預覽也可看到相同的內容如下:

https://ithelp.ithome.com.tw/upload/images/20240924/20151137mLwN1hiPyy.png

https://ithelp.ithome.com.tw/upload/images/20240924/2015113777VWI1xJz5.png

明天將介紹如何透過繼承與多型的方式,讓模型可更加彈性的被使用。


上一篇
[Day 10] TypeSpec:撰寫 OpenAPI 的領域特定語言 (四)
下一篇
[Day 12] TypeSpec:撰寫 OpenAPI 的領域特定語言 (六)
系列文
我要成為文件工程師── Web API 文件撰寫12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言