路由處理器

路由處理器讓您定義 Mirage 伺服器可以處理哪些 URL。

最簡單的路由處理器將 URL 對應到一個物件

this.get("/movies", { movies: ["Interstellar", "Inception", "Dunkirk"] })

現在,當您的應用程式向 /movies 發出 GET 請求時,它將收到此物件作為 JSON 資料。

如果您的 API 與應用程式位於不同的主機或埠,請設定 urlPrefix

    routes() {
      this.urlPrefix = 'https://127.0.0.1:3000';

您也可以透過傳入函數作為第二個引數來編寫函數路由處理器

this.get("/movies", (schema, request) => {
  return ["Interstellar", "Inception", "Dunkirk"]
})

函數路由處理器是編寫路由處理器最彈性的方式,因為它們讓您可以存取 Mirage 的資料層和請求物件。您的大部分路由處理器都會是函數。

您可以使用任何 HTTP 動詞來定義您的 API 路由。每個動詞方法都具有相同的簽名。第一個引數是路徑 (URL),第二個引數是傳回回應的函數。

this.get('/movies', () => { ... });
this.post('/movies', () => { ... });
this.patch('/movies/:id', () => { ... });
this.put('/movies/:id', () => { ... });
this.del('/movies/:id', () => { ... });
this.options('/movies', () => { ... });

時間控制

路由處理器的最後一個引數是一個選項物件,您可以使用它來調整時間控制。使用它來延遲特定路由的回應,並查看您的應用程式在與慢速網路通訊時的行為。

this.get(
  "/movies",
  () => {
    return ["Interstellar", "Inception", "Dunkirk"]
  },
  { timing: 4000 }
)

開發期間的預設延遲為 400 毫秒,測試期間為 0(因此您的測試執行速度很快)。

您也可以為所有路由設定全域時間參數。個別的時間參數會覆寫全域設定。

createServer({
  routes() {
    this.namespace = "api"
    this.timing = 2000

    this.get("/movies", () => {
      return ["Interstellar", "Inception", "Dunkirk"]
    })

    this.get(
      "/complex-query",
      () => {
        return [1, 2, 3, 4, 5]
      },
      { timing: 3000 }
    )
  },
})

如果您想為測試新增延遲,您可以透過將時間參數放在測試中來覆寫個別測試的時間

test("this route works with a delay", function () {
  server.timing = 10000

  // ...
})

由於伺服器會在每次測試後重設,因此此選項不會洩漏到您其餘的套件中。

存取資料層

路由處理器會接收 schema 作為其第一個參數,這讓它們可以存取 Mirage 的資料層

this.get("/movies", (schema) => {
  return schema.movies.all()
})

您的大部分路由處理器都會以某種方式與資料層互動。

第二個參數是 request 物件,其中包含您的應用程式發出之請求的相關資訊。例如,您可以從中存取動態 URL 段

this.get("/movies/:id", (schema, request) => {
  let id = request.params.id

  return schema.movies.find(id)
})

您也可以存取請求主體,例如處理包含應用程式傳送資料的 POST 或 PATCH 請求

this.post("/movies", (schema, request) => {
  let attrs = JSON.parse(request.requestBody)

  return schema.movies.create({ attrs })
})

normalizedRequestAttrs 輔助工具(如下所述)提供了一些處理請求資料的簡化方式。

動態路徑與查詢參數

注入到路由處理器的請求物件包含任何動態路由段和查詢參數。

若要定義具有動態段的路由,請在路徑中使用冒號語法 (:segment)。動態部分將透過 request.params.[segment] 提供

this.get("/authors/:id", (schema, request) => {
  let id = request.params.id

  return schema.authors.find(id)
})

也可以透過 request.queryParams.[param] 存取請求中的查詢參數。

狀態碼與標頭

預設情況下,Mirage 會根據路由使用的動詞設定回應的 HTTP 狀態碼

  • GET 為 200
  • PATCH/PUT 為 204
  • POST 為 201
  • DEL 為 204

如果 PATCH/PUT 和 POST 請求有回應主體,則會將狀態碼變更為 200。

此外,會將 Content-Type 的標頭設定為 application/json

您可以在路由處理函式中回傳 Response 類別的實例,來自訂回應碼和標頭。

import { createServer, Model, Response } from "miragejs"

createServer({
  models: {
    author: Model,
  },
  routes() {
    this.post("/authors", function (schema, request) {
      let attrs = JSON.parse(request.requestBody).author

      if (attrs.name) {
        return schema.authors.create(attrs)
      } else {
        return new Response(
          400,
          { some: "header" },
          { errors: ["name cannot be blank"] }
        )
      }
    })
  },
})

外部來源

您可以使用 Mirage 來模擬跨來源請求。預設情況下,像這樣的路由:

this.get('/contacts', ...)

會攔截向伺服您應用程式的相同來源發出的請求。若要處理不同的來源,請使用完整的網域名稱。

this.get('http://api.twitter.com/v1', ...)

如果您的整個應用程式使用外部(跨來源)API,您可以透過 urlPrefix 全域設定網域。

createServer({
  routes() {
    this.urlPrefix = 'https://my.api.com';

    // This route will intercept requests to https://my.api.com/contacts
    this.get('/contacts', ...)
  }
})

輔助工具

在編寫函式路由處理函式時,有幾個可用的輔助函式。

如果您是 Mirage 的新手,請不用擔心現在就理解這些。當您編寫更進階的路由處理函式時,它們會很有幫助。

serialize

此輔助函式在將指定的 Model 或 Collection 傳遞通過 Serializer 層之後,會回傳其 JSON。如果您想要在回傳序列化 JSON 之前對其進行一些最終處理,這會很有用。

// Note: Be sure to use function() here, rather than () => {}
this.get("/movies", function (schema) {
  let movies = schema.movies.all()
  let json = this.serialize(movies)

  json.meta = { page: 1 }

  return json
})

預設情況下,此方法會針對指定的 Model 或 Collection 使用指定的序列化器。您可以傳入特定的序列化器名稱作為第二個引數。

this.get("/movies", function (schema) {
  let movies = schema.movies.all()
  let json = this.serialize(movies, "sparse-movie")

  json.meta = { page: 1 }

  return json
})

您將在本指南的稍後章節中學到更多關於序列化器的資訊。

normalizedRequestAttrs

此輔助函式會以標準化形式回傳請求的主體,適用於使用和建立記錄。它本質上會移除 API 酬載中的格式設定邏輯,讓您取得基礎屬性,然後您可以使用這些屬性來修改 Mirage 的資料庫。

例如,如果您的應用程式使用以下資料發出 POST 請求:

// POST /users

{
  "data": {
    "type": "users",
    "attributes": {
      "first-name": "Conan",
      "middle-name": "the",
      "last-name": "Barbarian"
    },
    "relationships": {
      "team": {
        "data": {
          "type": "teams",
          "id": 1
        }
      }
    }
  }
}

那麼可以像這樣使用 normalizedRequestAttrs()

this.post("/users", function (schema, request) {
  let attrs = this.normalizedRequestAttrs()
  /*
    attrs is this object:
      {
        firstName: 'Conan',
        middleName: 'the',
        lastName: 'Barbarian',
        teamId: '1'
      }
  */
  return schema.users.create(attrs)
})

請注意,屬性鍵已轉換為駝峰式大小寫,並且已提取 team 外鍵。這是因為 user 擁有 team 外鍵;如果請求中包含另一個關係,但 user 沒有擁有其外鍵,則不會提取它。

此輔助方法會利用您序列化器的 normalize 方法。在上面的範例中,假設應用程式正在使用 JSONAPISerializer,它已經附帶了寫好的 #normalize 方法。如果您沒有使用任何綁定的序列化器,則需要實作 #normalize 並使其回傳 JSON:API 文件,才能使用此方法。

此外,您需要在此處使用完整的 function,而不是 ES6 箭頭函式(例如 () => { ... })。這是因為 normalizedRequestAttrs 需要來自函式處理函式的 this 內容,而箭頭函式會從外部範圍繫結 this

normalizedRequestAttrs() 依賴 modelName 來運作,並嘗試根據請求的 URL 自動偵測它。如果您使用傳統的 URL(例如,PATCH /users/1),則輔助函式應該可以運作。如果您使用自訂的 URL(例如,PATCH /users/edit/1),則需要將 modelName 作為第一個引數提供給輔助函式。

this.patch("/users/edit/:id", function (schema, request) {
  let attrs = this.normalizedRequestAttrs("user")
  // ...
})

這就是關於編寫低階函式路由處理函式的所有內容!

函式路由處理函式很靈活,但對於每個端點來說,編寫起來也很繁瑣。如果您正在使用足夠傳統的 API,希望您會編寫較少的函式路由處理函式和更多的簡寫。接下來讓我們討論這些!