Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 記錄軟體開發的點點滴滴 著重於微軟技術、網頁開發、DevOps、C#, Asp .net Mvc、Azure、AI、Chatbot、Docker、Data Science

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

2019-04-05 星期五
[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
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
2019-04-05 星期五
comments powered by Disqus