Fastify 系列教程三 (驗證、序列化和生命周期)


Fastify 系列教程:

驗證

Fastify 可以驗證請求信息,只有符合驗證規則的請求才會被處理。

JSON Schema

什么是 JSON Schema ,通俗來講,JSON Schema 就是“描述 JSON 數據格式的一段 JSON”。

首先,JSON Schema 也是一個 JSON 字符串,下面來看一個簡單的 JSON Schema:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "id": {
            "description": "The unique identifier for a product",
            "type": "integer"
        },
        "name": {
            "description": "Name of the product",
            "type": "string"
        },
        "price": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true
        }
    },
    "required": ["id", "name", "price"]
}

上面這段規則描述了這樣一個JSON:

1、type 表示該 JSON 的類型是一個 "object"。

type 的參數可以是:number, integer(整型), string, boolean, array, object 或者 null。也可以是一個包含上述類型的數組。

  1. schema: { "type": "number" }

    valid: 1, 1.5

    invalid: "abc", "1", [], {}, null, true

  2. schema: { "type": "integer" }

    valid: 1, 2

    invalid: "abc", "1", 1.5, [], {}, null, true

  3. schema: { "type": ["number", "string"] }

    valid: 1, 1.5, "abc", "1"

    invalid: [], {}, null, true

2、properties 定義了 JSON 的字段規則。

3、requirede 定義了必須存在的屬性列表。

我們來看一下可以用於這一模式中的各種重要關鍵字:

關鍵詞 描述
$schema $schema 關鍵字狀態,表示這個模式與 v4 規范草案書寫一致。
title 用它給我們的模式提供了標題。
description 關於模式的描述。
type type 關鍵字在我們的 JSON 數據上定義了第一個約束:必須是一個 JSON 對象。
properties 定義各種鍵和他們的值類型,以及用於 JSON 文件中的最小值和最大值。
required 存放必要屬性列表。
minimum 給值設置的約束條件,表示可以接受的最小值。
exclusiveMinimum 如果存在 "exclusiveMinimum" 並且具有布爾值 true,如果它嚴格意義上大於 "minimum" 的值則實例有效。
maximum 給值設置的約束條件,表示可以接受的最大值。
exclusiveMaximum 如果存在 "exclusiveMinimum" 並且具有布爾值 true,如果它嚴格意義上小於 "maximum" 的值則實例有效。
multipleOf 如果通過這個關鍵字的值分割實例的結果是一個數字則表示緊靠 "multipleOf" 的數字實例是有效的。
maxLength 字符串實例字符的最大長度數值。
minLength 字符串實例字符的最小長度數值。
pattern 如果正則表達式匹配實例成功則字符串實例被認為是有效的。

通過上面的配置,我們就可以驗證某個 JSON 是否符合要求了:

validate(JSONSchema, myJson)

有同學肯定會問,這個驗證函數 validate 從哪來?github 上有各種第三方驗證器:

語言 程序庫
C WJElement (LGPLv3)
Java json-schema-validator (LGPLv3)
.NET Json.NET (MIT)
ActionScript 3 Frigga (MIT)
Haskell aeson-schema (MIT)
Python Jsonschema
Ruby autoparse (ASL 2.0); ruby-jsonschema (MIT)
PHP php-json-schema (MIT). json-schema (Berkeley)
JavaScript Orderly (BSD); JSV; json-schema; Matic (MIT); Dojo; Persevere (modified BSD or AFL 2.0); schema.js.

而 Fastify 所使用的 ajv 也是一個 JSON Schema 驗證器,號稱:

The fastest JSON Schema validator for Node.js and browser with draft 6 support.

有了上面的介紹,我們就來看一下 Fastify 是怎么驗證請求信息的吧:

非常簡單,只需要添加需要驗證的字段即可。

  • body:驗證請求體,必須是 POST 或者 PUT 請求。
  • querystring: 驗證查詢字符串。可以是一個完成的 JSON Schema 對象(符合 {type: "object", properties: {...}} 的格式)或者沒有 typeproperties 屬性,而只有查詢字符串列表。(查看下面的例子)
  • params: 驗證路由參數。
  • headers: 驗證請求頭。

示例:

fastify.post('/add', {
  schema: {
    body: {
      type: 'object',
      properties: {
        name: {
          type: 'string'
        },
        id: {
          type: 'number'
        }
      },
      required: ['name', 'id']
    }
  }
}, function(request, reply){
  reply.send('validate successful')
})

當發送一個body為

{
  "name": "lavyun",
  "id": "hello"
}

post 請求時,會得到錯誤:

{
    "error": "Bad Request",
    "message": "[{\"keyword\":\"type\",\"dataPath\":\".id\",\"schemaPath\":\"#/properties/id/type\",\"params\":{\"type\":\"number\"},\"message\":\"should be number\"}]",
    "statusCode": 400
}

因為 id 不符合 number 類型,把 id 改成 1 就可以了。

注意:Fastify 配置了 avj 默認會自動把不符合類型的值強制轉換成規則中定義的類型,如果仍然不符合類型,則返回錯誤:

例如

{
  "name": null,
  "id": "2"
}

也會驗證通過,因為被強轉成:

  "name": "null",
  "id": 2

如果不想被強制轉換,可以通過配置 avj 關閉該功能:

const fastify = require('fastify')({
  ajv: {
    coerceTypes: false
  }
})

Schema Compiler

schemaCompiler 是一個指定 schema 編譯器的方法。(用來驗證 body, params, headers, querystring)。默認的 schemaCompiler 返回一個實現 ajv 接口的編譯器。

如果你想更改默認的 ajv 實例,可以傳入 ajv 配置項, 查看 Ajv documentation 了解更多。

或許想直接更換驗證的庫,比如使用 Joi:

const Joi = require('joi')

fastify.post('/the/url', {
  schema: {
    body: Joi.object().keys({
      hello: Joi.string().required()
    }).required()
  },
  schemaCompiler: schema => data => Joi.validate(data, schema)
})

序列化

通常,我們會通過 JSON 將數據發送給客戶端, Fastify 提供了一個強大的工具: fast-json-stringify,這是一個比原生 JSON.stringify() 還快的 JSON 格式化器,其原理就是通過配合 JSON Schema,快速定位字段的類型,省去了原生 JSON.stringify() 內部判斷字段類型的步驟,實現了 two times faster than JSON.stringify(). 的效果。

在路由選項中傳入了 output schema,fastify 就會使用它。

const schema = {
  response: {
    200: {
      type: 'object',
      properties: {
        value: { type: 'string' },
        otherValue: { type: 'boolean' }
      }
    }
  }
}

response schema 是基於狀態碼的,如果想應用相同的 schema 給多個同級狀態碼, 可以使用 2xx

const schema = {
  response: {
    '2xx': {
      type: 'object',
      properties: {
        value: { type: 'string' },
        otherValue: { type: 'boolean' }
      }
    },
    201: {
      type: 'object',
      properties: {
        value: { type: 'string' }
      }
    }
  }
}

patternProperties

fast-json-stringify 支持屬性匹配,符合屬性正則的字段都會被驗證:

const stringify = fastJson({
  title: 'Example Schema',
  type: 'object',
  properties: {
    nickname: {
      type: 'string'
    }
  },
  patternProperties: {
    'num': {
      type: 'number'
    },
    '.*foo$': {
      type: 'string'
    }
  }
})
 
const obj = {
  nickname: 'nick',
  matchfoo: 42,
  otherfoo: 'str'
  matchnum: 3
}
 
console.log(stringify(obj)) // '{"matchfoo":"42","otherfoo":"str","matchnum":3,"nickname":"nick"}'

更多 fast-json-stringify 的使用可以查看文檔

生命周期

Fastify 嚴格遵循內部生命周期的架構。在每個部分的右側分支上都有生命周期的下一個階段,左側的分支上有相應的錯誤狀態碼,如果父代引發錯誤,則會生成相應的錯誤狀態碼(注意,所有錯誤都由Fastify自動處理)。

Fastify 生命周期圖示:

Incoming Request (請求到達)
  │
  └─▶ Instance Logger (實例化 Logger)
        │
        └─▶ Routing (路由匹配)
             │
       404 ◀─┴─▶ onRequest Hook (onRequest鈎子)
                  │
        4**/5** ◀─┴─▶ run Middlewares (執行中間件)
                        │
              4**/5** ◀─┴─▶ Parsing (解析請求對象)
                             │
                       415 ◀─┴─▶ Validation (驗證)
                                   │
                             400 ◀─┴─▶ preHandler Hook (preHandler鈎子)
                                         │
                               4**/5** ◀─┴─▶ beforeHandler
                                               │
                                     4**/5** ◀─┴─▶ User Handler
                                                     │
                                                     └─▶ Reply (響應)
                                                          │ │
                                                          │ └─▶ Outgoing Response (發出響應)
                                                          │
                                                          └─▶ onResponse Hook (onResponese鈎子

Fastify 的更多使用將在接下來的博客中說明。

參考文檔:
JSON 模式 / http://wiki.jikexueyuan.com/project/json/schema.html

Tips:訪問 https://lavyun.gitbooks.io/fastify/content/ 查看我翻譯的 Fastify 中文文檔。

訪問lavyun.cn 查看我的個人博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM