iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Software Development

程式設計沒有告訴你的事:30 天破解每一個 Why系列 第 23

Handler 回傳的東西都不一樣,Framework 怎麼把它們變成 HTTP Response?

  • 分享至 

  • xImage
  •  

昨天我們一路研究到 Parameter Binding。

也就是:

Framework 先透過 Reflection 看 Handler 需要什麼參數,

再從:

Route

Query

Header

Body

這些地方找到資料,最後轉成正確的 Type。

所以現在 Framework 已經有機會做到:

Request

找到 Handler

準備 Parameters

執行 Handler

但這時又出現一個新的問題。

Handler 執行完之後,到底會回傳什麼?

我們之前自己的 Mini Web Framework 很簡單。

Handler 幾乎都是:

Func

所以 Framework 可以直接假設:

Handler 執行完

一定得到 HttpResponse

但真正的 Web Framework 不可能限制所有 Handler 都只能回傳同一種東西。

有些 Handler 可能回傳:

string

有些可能回傳:

User

有些可能回傳:

List

有些可能只想回:

404

甚至有些 Handler 可能根本沒有要回傳內容。

那 Framework 要怎麼把這些不同的結果,最後全部變成 Browser 看得懂的 HTTP Response?

這就是今天想理解的問題。

💭 我原本以為……

以前看到:

return "Hello";

我會覺得:

這就是 Response。

看到:

return user;

也會覺得:

Framework 就把 user 回傳出去。

但 Day14 已經知道,其實 Browser 真正收到的不是:

C# string

也不是:

C# User Object

而是 HTTP Response。

例如:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: ...

Hello

所以真正的流程應該不是:

Handler

直接送給 Browser

而是:

Handler

Return Value

Framework 處理

HttpResponse

Browser

這中間還有一層轉換。

Handler Return Value 到底是什麼?

假設 Handler 是:

GetHello()

回傳:

"Hello"

那 Framework 得到的是:

string

如果 Handler 是:

GetUser()

回傳:

User

Framework 得到的則是一個 User Object。

如果 Handler 是:

GetProducts()

回傳:

List

那又是另一種 Type。

所以 Framework 執行 Handler 之後,不能直接假設:

這一定是一份完整的 HTTP Response。

比較合理的做法是:

先取得 Handler Result

再根據 Result 的 Type 決定怎麼轉換。

例如:

string

text/plain

User Object

JSON

List

JSON Array

HttpResponse

直接送出

null

可能是空 Response

這就是 Framework 又多出來的一層工作。

為什麼 string 不能直接送?

表面上看:

"Hello"

確實最後也會出現在 Browser。

但 HTTP Response 還需要:

Status Code

Headers

Content-Type

Content-Length

Body

所以 Framework 如果收到:

"Hello"

至少還要幫它補成:

Status = 200

Content-Type = text/plain

Body = Hello

再由 HttpResponse 負責真正送出去。

所以:

string

只是 Handler Result。

不是完整的 HTTP Response。

Object 又怎麼辦?

假設 Handler 回傳:

User

裡面有:

Name = Andy

Age = 20

Browser 不認識 C# 的 User Object。

所以 Framework 必須先把它轉成一種可以傳輸的格式。

最常見的就是 JSON。

例如:

User Object

Serialization

{"name":"Andy","age":20}

接著才能建立:

HTTP/1.1 200 OK
Content-Type: application/json
...

{"name":"Andy","age":20}

所以前幾天學到的 Serialization,今天又接回來了。

Day20 研究的是:

JSON

C# Object

這叫 Deserialization。

今天剛好反方向:

C# Object

JSON

這就是 Serialization。

整個來回開始變成:

Client

JSON

Deserialization

C# Object

Handler

C# Object

Serialization

JSON

Client

HttpResponse 又出現了

Day15 我們自己建立過:

HttpResponse

裡面有:

StatusCode

StatusText

ContentType

Body

所以現在一個很自然的設計就是:

不管 Handler 回傳什麼,

Framework 最後都先把它轉成:

HttpResponse

例如:

string

HttpResponse

User

JSON

HttpResponse

404 Result

HttpResponse

最後全部統一:

HttpResponse.Send()

這樣 Browser 那一端就不需要知道 Handler 原本到底回傳什麼。

所以可以整理成:

Handler

Result

Result Processing

HttpResponse

Send

Browser

這其實就是統一出口。

為什麼 Framework 需要統一出口?

假設每種 Handler Result 都自己處理 NetworkStream。

那就會變成:

string Handler

自己 Write

User Handler

自己 JSON Serialize

自己 Write

404 Handler

自己組 404

自己 Write

最後所有底層細節又會散回各個地方。

但如果全部先轉成:

HttpResponse

就可以變成:

各種 Result

統一轉換

HttpResponse

同一套 Send()

這樣 Response Pipeline 才會比較乾淨。

Result Type 可以怎麼分類?

對我們自己的 Mini Web Framework 來說,一開始不用支援太多。

可以先想像幾種情況。

第一種:

string

例如:

return "Hello";

Framework 可以轉成:

StatusCode = 200

ContentType = text/plain

Body = Hello

第二種:

普通 C# Object

例如:

return user;

Framework 可以:

Serialize 成 JSON

ContentType = application/json

StatusCode = 200

第三種:

HttpResponse

如果 Handler 本身已經回傳:

HttpResponse

那 Framework 就可以直接使用。

這樣最小版本其實就已經很有 Framework 的感覺了。

Result Conversion 是什麼?

目前可以把它理解成:

把 Handler 回傳的東西,轉成統一 HttpResponse 的過程。

例如:

Result = "Hello"

判斷 Type 是 string

建立 Text Response

Result = User

判斷是 Object

Serialize JSON

建立 JSON Response

Result = HttpResponse

直接使用

也就是:

object result

ConvertResult(...)

HttpResponse

這會是一個很重要的 Framework 元件。

Reflection 又可以派上用場嗎?

可以。

Framework 執行 Handler 後,可以知道:

Return Type

也可以看到實際得到的:

Result Object

例如:

result.GetType()

可能得到:

System.String

User

List

HttpResponse

Framework 就可以根據不同 Type 決定轉換策略。

當然,真正 Framework 的做法可能更完整。

例如:

Result Interface

Result Executor

Formatter

Serializer

Content Negotiation

等等。

但我們現在先理解最小版本:

不同 Return Value

判斷

轉成 HttpResponse

就足夠了。

Content-Type 為什麼又出現?

因為不同的結果,Browser 要用不同方式理解。

如果 Handler 回傳:

Hello

可以是:

Content-Type: text/plain

如果回傳:

{"name":"Andy"}

則應該是:

Content-Type: application/json

如果未來回 HTML:

則可能是:

Content-Type: text/html

所以 Result Conversion 不只決定:

Body 是什麼

還要決定:

Content-Type 是什麼

這樣 Browser 才知道收到的資料應該怎麼解讀。

Status Code 又怎麼決定?

最簡單情況下:

Handler 正常回傳結果

200 OK

找不到 Route

404 Not Found

Request 格式錯誤

400 Bad Request

Server 執行出錯

500 Internal Server Error

所以 Result Conversion 最後也會和 Status Code 有關。

不過對 Mini Web Framework 來說,目前不用一次處理全部。

先做到:

正常 → 200

找不到 → 404

就足夠建立核心概念。

📚 官方怎麼說?

在 ASP.NET Core 中,Endpoint 或 Action 執行後會產生結果,而 Framework 會負責把結果寫入 HTTP Response。

不同開發模式中可能會看到:

string

IResult

IActionResult

Object Result

JSON Result

等等。

雖然細節不同,但核心概念很接近:

Handler 執行結果

Framework 處理

HTTP Response

也就是說,Handler 的 Return Value 和真正送到 Client 的 HTTP Response,並不是完全相同的東西。

中間還存在 Framework 的 Response Processing。


以前的我:

return user

Browser 收到 user

現在的我:

Handler

return User Object

Framework

Serialization

JSON

HttpResponse

HTTP bytes

Browser

所以:

return

只是把結果交回 Framework。

真正把結果送到 Browser 的,是 Framework 後面的 Response Pipeline。

這讓我重新理解:

Handler 並不是直接和 Browser 溝通。

Handler 其實是在和 Framework 溝通。


今天最大的發現是:

Framework 在 Handler 前後,其實各有一個轉換工作。

Handler 前:

HTTP Request Data

Parameter Binding

C# Parameters

Handler 後:

C# Return Value

Result Conversion

HTTP Response

也就是:

HTTP 世界

Framework

C# 世界

Handler

C# 世界

Framework

HTTP 世界

Framework 真正站在兩個世界中間。

這讓整個 Web Framework 的角色越來越完整。

Framework 執行 Handler 後,先取得 Return Value。

接著依照 Result 的 Type,決定要怎麼處理。

例如 string 轉成文字 Response,Object 轉成 JSON Response。

最後所有結果都可以統一整理成 HttpResponse,再由 Framework 寫回 Browser。

🛠 與 Mini Web Framework 的關聯

現在整個 Mini Web Framework 的方向已經逐漸完整:

Browser

HttpRequest

Routing

Reflection

Parameter Binding

Handler

Return Value

Result Conversion

HttpResponse

Browser

前面我們建立的 HttpResponse,今天開始有更明確的定位。

它不只是:

自己手動建立的一個 Response Class。

而可以成為:

所有 Handler Result 最後共同轉換到的格式。

也就是:

string
Object
Error
其他結果

HttpResponse

這會讓整個 Response Pipeline 變得更一致。

今天暫時不實作

目前依然先把核心觀念整理清楚。

因為前面的 HttpRequest、Headers、Query、Body、JSON、Reflection、Parameter Binding 還需要在最後幾天整合。

之後真正實作 Result Conversion 時,可以先從兩種最簡單情況開始:

string

text/plain HttpResponse

普通 Object

JSON HttpResponse

再讓:

HttpResponse.Send()

統一負責送回 Browser。

這樣就足夠做出一個很有 Framework 感的最小版本。

結尾

前幾天我們一直在研究:

Framework 怎麼把 Request 變成 Handler 可以使用的資料。

到了今天,方向終於反過來。

Handler 執行完後,Framework 又必須把 C# 世界的結果轉回 HTTP 世界。

所以整個流程可以非常漂亮地對稱:

HTTP Request

HttpRequest

Parameter Binding

C# Parameters

Handler

C# Result

Result Conversion

HttpResponse

HTTP Response

這讓我開始真正理解:

Framework 並不是只有 Routing。

Routing 只是在中間找到「誰來處理」。

Handler 前後其實還有很多工作。

前面負責:

把 HTTP 資料變成程式資料。

後面則負責:

把程式結果變回 HTTP 資料。

這兩邊一起完成,才是真正完整的 Request / Response Pipeline。


現在 Request 已經可以想像一路走到 Handler,再從 Handler 變成 Response。

但目前所有東西還是像一條固定流程。

如果未來每一個 Request 都想先經過:

Logging

Authentication

Exception Handling

Timing

CORS

是不是每個 Handler 都要自己寫一次?

真正的 Framework 又是怎麼讓 Request 在到達 Handler 前後,經過一層一層共用處理?


上一篇
Handler 的參數到底從哪裡來?Framework 怎麼做 Parameter Binding?
下一篇
每個 Request 都要做 Logging 和驗證嗎?Middleware 到底是什麼?
系列文
程式設計沒有告訴你的事:30 天破解每一個 Why24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言