iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Modern Web

Fastify 101系列 第 11

[Fastify] Day11 - FastifyRequest

  • 分享至 

  • xImage
  •  

大家好,我是 Yubin

在定義 route 的時候,我們會需要定義那個 route 的 hander。而 handler 的第一個參數,就是 FastifyRequest。
這篇文章來談談 FastifyRequest 這個核心物件,以及如何用 TypeScript 來開發。


FastifyRequest

每個 request 進來,都會被封裝成 FastifyRequest,我們可以透過 FastifyRequest 拿到我們想要的資訊。

  • query,Query String 物件。
  • body,Request Payload 物件,若 Payload 是 JSON 格式則會轉換成 JavaScript 物件。
  • params,Url 中的 Parameters。
  • headers,HTTP Header 物件,可以取值或設定想要的 header,Type 定義在 NodeJS 的 http 模組裡的 IncomingHttpHeaders,想要拿特定的 header 可以用 request.headers['content-type'] 的方式取得。
  • raw,NodeJS 核心的 HTTP Request 物件,Type 定義在 NodeJS 的 http 模組中的 IncomingMessage
  • server,拿到 FastifyInstance。
  • id,Request ID。
  • log,拿到 Logger Instance,可以利用 request.log.info() 的方式來寫 log。
  • ip,該 request 的 ip address。
  • ips,ip 的陣列,順序從進到遠,只有在 server 的 trustProxy 打開的情況下有值。
  • hostname,該 request 的 host 欄位。
  • protocol,該 request 所使用的通訊協定。(httphttps)
  • method,該 request 的 HTTP Method。
  • url,該 request 的 Url。
  • routerMethod,該 route handler 定義的 HTTP Method。
  • routerPath,該 route handler 定義的 Path Pattern。(注意是 Pattern,不是完整的 Path)
  • is404,若為 true,則會交給 404 handler 處理。
  • socket,拿到底層的 socker,Type 為 NodeJS net 模組裡的 Socket

假設有一個 Endpoint 如下

server.post('/hello/:name', (request, reply) => {
  // code
})

透過 API Client (這邊使用 Postman),發送一個像這樣的 request 給伺服器

https://ithelp.ithome.com.tw/upload/images/20220926/201511481tnvx5xBaY.jpg

來看一下這個打出去 request。

HTTP Method 是 POST
Url 是 http://localhost:8888/hello/yubin?key01=value01&key02=value02
Payload 是 { "age": 28 }

我們把 Url 拆開來看,

Protocol,通訊協定是 http
Host,主機是 localhost:8888
Url 是 /hello/yubin?key01=value01&key02=value02
Url 中,/hello/yubin 是 path,?key01=value01&key02=value02 是 query string。


Url Parameter

如果想定義的 route 中,Path 會有一些參數。

比如說 /hello/yubin,希望把 /hello/後面的字串當成另一個參數 name,
在定義 route 的時候可以用 /hello/:name 的方式。

當有一個 request 進來,如果符合這樣的 Pattern,就會進到 /hello/:name 的 handler 裡面。

如果想要拿到 Url 中的 Parameters,可以利用 request.params,拿到各個 parameters。

server.post('/hello/:name', (request, reply) => {
  const name = request.params.name

  return reply.status(200).send({ message: `Hello ${name}` })
})

With TypeScript

但實際在開發的時候,會發現 TypeScript 跳警告。

https://ithelp.ithome.com.tw/upload/images/20220926/20151148rpicefFmcU.jpg

request.params 的型態是 unknown,無法取得 name 這個欄位。

雖然我們知道進來這個 handler 的 request.params 中會有 name 這個欄位,但 Fastify 把 request.params 定為 unknown,目的是要開發者明確定義 request.params 到底是什麼型態。

可以創建一個 Type 後,用 as 的方式,給 request.params 一個 Type。

interface NameParams {
  name: string
}

server.post('/hello/:name', (request, reply) => {
  const params = request.params as NameParams
  const name = params.name

  return reply.status(200).send({ message: `Hello ${name}` })
})

或是把 Type 定義宣告在 .post 那邊,如下範例程式。

interface NameParams {
  name: string
}

server.post<{Params: NameParams}>('/hello/:name', (request, reply) => {
  const name = request.params.name

  return reply.status(200).send({ message: `Hello ${name}` })
})

這也是自己比較喜歡的方式,定義在 route scope,整個 Handler 要拿 Parameters 都可以透過 request.params 來取得。

因為有詳細定義 Type,vscode 或 TypeScript Engine 也會提示你可以用的欄位,增加寫程式的便利性與正確性。

Query String

要拿到 query string 可以透過 request.query

如果期望的 query string 會長這樣進來 ?key01=value01&key02=value02

interface MyQueryString {
  key01: string
  key02: string
}

server.post<{ Querystring: MyQueryString }>('/hello/:name', (request, reply) => {
  const key01 = request.query.key01
  return reply.status(200).send({ message: `Hello ${key01}` })
})

一樣也可以 request.query as 的方式指定 Type

Body

要拿到 Request Payload 可以透過 request.body

如果期望的 payload 是長這樣 { "age": 28 }

interface MyBody {
    age: number
}

server.post<{ Body: MyBody }>('/hello/:name', (request, reply) => {
  const age = request.body.age
  return reply.status(200).send({ message: `Age is ${age}` })
})

一樣可以用 request.body as 的方式指定 Type

Headers

預設的 request.headers 的 Type 為 IncomingHttpHeaders,如果有一些自訂的 header 要拿,可以自己定義 Type。

interface MyHeaders {
  'My-Header01': string
}

server.post<{ Headers: MyHeaders }>('/hello/:name', (request, reply) => {
  const header01 = request.headers['My-Header01']
  return reply.status(200).send({ message: `Header01 is ${header01}` })
})

一樣可以用 request.headers as 的方式指定 Type


以上為 FastifyRequest 的介紹以及用 TypeScript 開發的方式。

雖然感覺要多定義一些 Type 才能開發,但藉由強型別的特性,在開發階段讓編輯器或 TypeScript Compiler 幫我們做型別的檢查,降低人為出錯的機會 (例如打錯字),可以讓你寫程式寫得更安心順暢。


上一篇
[Fastify] Day10 - Lifecycle
下一篇
[Fastify] Day12 - FastifyReply
系列文
Fastify 10130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言