Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[apiary][05]設計API時好用的工具 - 讓前後端溝通格式不再卡卡 - MSON格式篇

2019-04-05 Friday
[apiary][05]設計API時好用的工具 - 讓前後端溝通格式不再卡卡 - MSON格式篇.jpg
圖片來源:https://pixabay.com/en/books-spine-colors-pastel-1099067/,logl來源:https://apiary.io/

在上一篇([apiary][04]設計API時好用的工具 - 讓前後端溝通格式不再卡卡 - API Blueprint Actions格式篇)介紹了如何定義Actions,換句話説,以目前的知識要定義出一個API已經沒有什麽問題了。

但是在上一篇還有遇到一個問題,那就是不管是request或者response的JSON物件沒有定義出來裡面的欄位以及説明。換句話説,目前的方式只有範例,但是每一個欄位的形態,描述沒有辦法定義。

Apiary團隊當初考慮到了這一個問題,因此建立出一個格式稱之爲Markdown Syntax for Object Notation (MSON),專門用來描述JSON以及JSON Schema。

這邊來看看MSON怎麽使用。

MSON是什麽

MSON的全稱是:Markdown Syntax for Object Notation ,白話來説,就是可以用類似markdown的語法定義出JSON以及JSON Schema,因此可以透過這個語法定義好request以及response的物件訊息。

MSON雖然主要是用在API Blueprint裡面定義格式,不過MSON本身也是一個open standard的格式(RFC 2119),換句話説API Blueprint和MSON可以分開兩塊來看。

接下來,先看MSON的格式如何定義JSON,然後在看看怎麽在API Blueprint裡面透過Attributes來使用。

MSON的基本格式

這邊一樣沿用之前的例子,如果說今天要定義一筆ToDo的格式看看會怎麽用MSON來定義。

這邊拆幾個情景來看:

  1. 基本物件定義方式
  2. 欄位是另外一個複雜物件
  3. 内建特殊形態:如何定義array
  4. 内建特殊形態:如何定義enum

基本物件定義方式

假設今天最後的JSON結果是:

{
	"id": 1,
	"title": "要買牛奶"
}

那麽在MSON裡面就會是:

+ id: 1 (number, required) - 資料的Id
+ title: `要買牛奶` (string, required) - todo 的描述

從這個範例可以看出,一個欄位的結構大概是:+ {欄位名稱}: {範例值} ({形態}, {required | optional}) - {欄位描述}

和昨天介紹到的Parameters的寫法結構一樣。

欄位是另外一個複雜物件

基本的物件定義的出來了,如果說今天要複雜一點呢?

舉例來説,假設今天todo要多分類的概念,可以讓使用者自訂分類,這個時候就要多欄位出來。

假設最後的json物件是:

{
  "id": 1,
  "title": "要買牛奶",
  "category": {
    "id": 1,
    "name": "要買"
  }
}

那麽MSON就會是:

+ id: 1 (number, required) - 資料的Id
+ title: `要買牛奶` (string, required) - todo 的描述
+ category
	+ id: 1 (number, required) - 分類的Id
	+ name: `要買` (string, required) - 分類名稱

從上面的範例可以看出,如果一個type是另外一個物件(以範例來説是category),那麽只需要把裡面的property前面先加一個tab,其他同一般欄位定義方式一樣即可。

内建特殊形態:如何定義array

能夠定義欄位,以及欄位是另外一個物件的方法了之後,接下來在介紹兩個比較特別的形態:enum以及array

首先來看array,假設今天分類可以有多筆,這個時候就要變成array了。假設最後的json是:

{
  "id": 1,
  "title": "要買牛奶",
  "categories": [
    {
      "id": 1,
      "name": "要買"
    }
  ]
}

那麽對應的MSON就會變成:

+ id: 1 (number, required) - 資料的Id
+ title: `要買牛奶` (string, required) - todo 的描述
+ categories (array)
	+ (object)
		+ id: 1 (number, required) - 分類的Id
		+ name: `要買` (string, required) - 分類名稱

這次的修改有:

  1. category改成了categories表示是複數
  2. 由於變成了array,因此categories的形態特別定義爲array
  3. 由於array裡面的物件不需要欄位名稱,因此array下面的第一個property只有定義為object,但是沒有property名稱
  4. (object)下面定義的就是物件的欄位

内建特殊形態:如何定義enum

接下來看看另外一個特殊形態,enum

有時候有些欄位只有特定幾個值,這個時候會用enum避免值有錯。

以目前的例子,假設需要一個欄位代表目前這個todo的狀態,那麽json的範例會是:

{
  "id": 1,
  "title": "要買牛奶",
  "state": "0"
}

MSON定義起來會是:

+ id: 1 (number, required) - 資料的Id
+ title: `要買牛奶` (string, required) - todo 的描述
+ state: 0 (enum)
	+ 0 - 表示這個task已經完成
	+ 1 - 表示這個task已經過期
	+ 2 - 表示這個task尚未開始

MSON:客制形態,讓形態可以被重複使用

上面的部分已經把怎麽用MSON定義物件的方式介紹完了,不過就算改成了MSON讓定義變得容易並且簡單,但是不好重複使用。

舉例來説,今天取得1筆ToDo得到的物件,和取多筆得到的物件可能從欄位來説是一樣的,那要重複寫兩次不是很傻?更別説如果需要調整,到時候修改很容易改到了一個但是漏掉了另外一個

有沒有方式可以把定義好的MSON變成一個客制的形態?

這邊舉一個例子,假設categories想要建立一個形態專門描述,那麽可以:

定義一個Resource叫做Data Structures裡面有一個形態叫做Category

接下來在API Blueprint的文件 最下面增加一個區塊叫做Data Structures - 這是一個特殊的區塊用來定義客制形態用。

Data Structures下面建立一個形態叫做Category

# Data Structures

## Category

+ id: 1 (number, required) - 分類的Id
+ name: `要買` (string, required) - 分類名稱
修改定義回傳的時候欄位categories,改成回傳一個array[Category]

最後的樣子是:

+ id: 1 (number, required) - 資料的Id
+ title: `要買牛奶` (string, required) - todo 的描述
+ state: 0 (enum)
	+ 0 - 表示這個task已經完成
	+ 1 - 表示這個task已經過期
	+ 2 - 表示這個task尚未開始
+ categories (array[Category])

從上面可以看到,categories從本來一個array object (預設沒有給形態就是object),變成是一個array Category。

透過這種定義客制形態,只需要定義一次,在每個地方就可以重複使用,這樣未來就算要調整,只需要改一個地方即可。

MSON:形態繼承

上面的客制形態讓我們可以重複使用一個已經定義好的形態,避免到處copy and paste。

但是很有可能有些形態很類似,只是有部分欄位不同而已,這種情況怎麽辦?

從程式的角度來説,這個時候繼承就很適合做這種事情,而剛好MSON也有支援這種方式。

假設,未來定義的api不管回傳什麽都有一個固定的格式,例如:

{
  "statusDescription": "succes",
  "result": {
    // 看回傳内容
  }
}

那麽可以用MSON的Data Structure定義出:

一個客制形態叫做Success,代表只要成功都一定要有的欄位

首先定義一個Success的形態,表示所有的欄位都要包含。

然後建立一個ToDoRead的形態,繼承Success

## Success

+ statusDescription: succes
+ result

## ToDoRead (Success)

+ result
    + id: 1 (number, required) - 資料的Id
    + title: `要買牛奶` (string, required) - todo 的描述
    + state: 0 (enum)
        + 0 - 表示這個task已經完成
        + 1 - 表示這個task已經過期
        + 2 - 表示這個task尚未開始
    + categories (array[Category])

## Category

+ id: 1 (number, required) - 分類的Id
+ name: `要買` (string, required) - 分類名稱

以上面例子來説,只要用到ToDoRead,就一定會有欄位statusDescription,以及result,因爲是繼承下來的,因此也可以覆寫。

整合到API Blueprint文件裡面 - 使用 Attributes

MSON的介紹就到了這邊,基本上上面的如果都熟悉的話,任何JSON定義都沒有什麽問題。

接下來就是要怎麽在API Blueprint裡面使用定義出來的MSON形態。

要使用很簡單,本來上一篇是直接在Response裡面放入JSON,現在要用MSON只需要改成使用關鍵字Attributes即可。

以今天的例子來説,最後結果會是:

### 取得單筆TODO [GET /api/todo/{todoId}]

取得單筆todo資料

+ parameters
    + todoId: 10 - todo 的id

+ Response 200 (application/json)

    + Attributes (ToDoRead)

最後在文件呈現的樣子會是:

Untitled.png
文件呈現樣子

從上面截圖可以看到,每一個欄位的描述,以及一些範例的值。

如果今天沒有自訂形態,那麽Attributes就不需要括弧定義使用的形態,直接在下面寫上面提到的欄位定義語法即可。

結語

這一篇介紹了MSON這個語法,透過MSON不只可以定義欄位的一些描述以及形態,還可以定義出客制形態並且透過繼承方式讓形態可以重複使用。

到這邊爲止,基本上怎麽用apiary定義api就沒有什麽問題了,接下來看另外一個功能,也就是mock的功能。

寫好的文件可以讓apiary自動產生mock,讓前端可以直接串接mock開發。

下一篇([apiary][06]設計API時好用的工具 - 讓前後端溝通格式不再卡卡 - 前端如何使用mock server以及inspector來開發)來看看怎麽用apiary提供的mock。

參考資料

MSON的官方github
https://github.com/apiaryio/mson
API Blueprint的官方MSON tutorial
https://apiblueprint.org/documentation/mson/tutorial.html

如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
2019-04-05 Friday
comments powered by Disqus