iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0

昨天,我第一次建立了自己的 HttpResponse。

原本所有 Response 相關的資訊都散落在 Program.cs:

StatusCode
StatusText
ContentType
Body
HTTP 格式
Encoding
NetworkStream.Write()

最後則整理成:

HttpResponse
├── StatusCode
├── StatusText
├── ContentType
├── Body
└── Send()

做到這裡後,再回頭看 Request,突然覺得很奇怪。

因為目前 Request 還是這樣處理:

先把收到的 bytes 轉成字串,再找出第一行,接著用空白切開,最後分別取得 Method、Path 和 Version。

也就是說:

Response 已經是一個有結構的物件,但 Request 還是一大堆散落的字串處理。

那既然 Framework 可以把 Response 包成 HttpResponse,Request 為什麼不能也變成 HttpRequest?

所以今天的問題就是:

Framework 為什麼需要自己的 Request 物件?

💭 我原本以為……

以前使用 ASP.NET Core 時,很習慣直接寫:

Request.Method

或:

Request.Path

感覺 Method、Path、Headers、Query、Body,好像本來就已經存在於 Request 裡。

但前幾天自己從 Socket 開始做後,才知道 Browser 真正送來的一開始其實只是網路上的資料。

例如:

GET /products HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: …
Accept: …

我們之前甚至還必須自己:

收到 bytes

轉成 string

找到 Request Line

Split

取得 Method / Path / Version

所以現在才發現:

Request.Path 並不是 Browser 直接傳來的一個 C# Property。

它是 Framework 先理解原始 HTTP Request 之後,才整理出來的結果。

回頭看現在的 Program.cs

目前 Program.cs 大概同時負責:

Program.cs
├── 等待 TCP Connection
├── 讀取 bytes
├── bytes → string
├── 找 Request Line
├── Split Request Line
├── 取得 Method
├── 取得 Path
├── 取得 Version
├── Routing
└── 回傳 HttpResponse

昨天我們才剛把 Response 的責任拆出去。

現在 Request 這邊卻還全部擠在 Program.cs。

尤其是目前的程式只有我們自己知道:

parts[0] → Method
parts[1] → Path
parts[2] → Version

如果未來其他地方也想取得 Path,難道每個地方都要重新記得 parts[1] 嗎?

顯然不太合理。

🧠 如果有 HttpRequest,會變成什麼?

我想要的是,把目前的:

GET /products HTTP/1.1

經過解析後變成:

HttpRequest
├── Method = GET
├── Path = /products
└── Version = HTTP/1.1

這樣其他程式就不必知道 Request Line 怎麼 Split。

只需要:

request.Method
request.Path
request.Version

例如 Routing 現在是拿 path 去查 Route Table。

未來就可以變成:

request.Path

也就是:

原始 HTTP

HttpRequest

Routing

而不是:

原始 HTTP

一堆 string / array

Routing

HttpRequest 第一版應該包含什麼?

我們目前其實還不用一次把 Request 做得很完整。

先從 Day12 已經理解的三個東西開始:

HttpRequest
├── Method
├── Path
└── Version

例如 Browser 傳:

GET /hello HTTP/1.1

最後得到:

Method = GET
Path = /hello
Version = HTTP/1.1

這和我們 Day12 做過的事情一模一樣。

差別只是:

以前解析結果散落在三個變數,現在開始把它們視為「同一個 Request 的資料」。

那 Parsing 應該放在哪裡?

這又產生另一個問題。

如果建立 HttpRequest,但 Program.cs 還是自己 Split,那其實只完成一半。

比較理想的方向應該是:

raw HTTP Request

HttpRequest.Parse(…)

HttpRequest

例如未來可能會變成:

HttpRequest request = HttpRequest.Parse(rawRequest);

這樣 Program.cs 就不需要知道:

第一行怎麼取得
空白怎麼拆
哪個 index 是 Path

這些 Parsing 細節。

只要知道:

把原始 Request 交給 HttpRequest,最後拿回整理好的 Request 物件。

為什麼 Parse() 可以是 static?

未來實作時,我預計會寫成類似:

public static HttpRequest Parse(string rawRequest)

這裡的 static 代表這個方法是屬於 HttpRequest 這個 Type,而不是某一個已經存在的 HttpRequest Instance。

所以可以直接:

HttpRequest.Parse(rawRequest)

而不是:

new HttpRequest().Parse(…)

放在今天的情境就很好理解:

現在還沒有 HttpRequest 物件

手上只有 rawRequest

呼叫 HttpRequest.Parse(…)

建立 HttpRequest

所以 Parse() 很適合先作為「從原始資料建立 Request」的入口。

📚 官方怎麼說?

完成目前的設計思考後,我去看了 ASP.NET Core 的 HttpRequest。

Microsoft 官方文件中,HttpRequest 代表一個 HTTP Request,並提供 Request 相關資訊,其中包含 Method、Path、Headers、Host 等成員。

這和我們目前想做的方向非常接近。

我們現在預計先做:

Method
Path
Version

真正 ASP.NET Core 的 Request 當然遠遠不只這些。

它還有例如:

Headers
Host
Query
Scheme
Body
Cookies
ContentType
ContentLength
……

所以今天自己設計的 HttpRequest,不是要複製 ASP.NET Core。

而是從最小版本理解:

Framework 為什麼需要先把原始 HTTP 資料,整理成 Request 抽象。

為什麼不直接永遠使用原始字串?

理論上當然可以。

例如每次想知道 Path,就重新從 rawRequest 裡找第一行,再 Split,最後取得第二個位置。

確實也能跑。

但問題是,其他程式會開始依賴 HTTP 原始格式。

例如:

Router

知道 HTTP 格式

Handler

知道 HTTP 格式

其他功能

也知道 HTTP 格式

這樣 HTTP Parsing 的細節就會散落到整個 Framework。

如果有 HttpRequest,則可以變成:

Raw HTTP

HttpRequest Parsing

HttpRequest
├── Method
├── Path
└── Version

其他元件使用

也就是:

只有負責 Parsing 的地方需要理解原始 HTTP 格式。

其他地方只需要理解 HttpRequest。

🧠 今天的認知更新

以前的我:

Browser 傳 Request

Framework 得到 Request

現在這句話可以拆得更細:

Browser

TCP

bytes

原始 HTTP Request

Parsing

HttpRequest

Routing / Handler

也就是說:

Request 物件其實是 Framework 建立出來的一層抽象。

Browser 並沒有把 new HttpRequest() 傳過來。

Browser 傳的是符合 HTTP 規則的資料。

Framework 收到後,再把它轉成開發者比較方便使用的物件。

Request 與 Response 開始對稱了

做到 Day15 和 Day16,我發現整個架構開始慢慢變得比較整齊。

之前:

Request → 一大堆 string
Response → 一大堆 string

Day15:

Request → 一大堆 string
Response → HttpResponse

Day16 想完成:

Request → HttpRequest
Response → HttpResponse

也就是:

Browser

Raw Request

HttpRequest

Framework

HttpResponse

Raw Response

Browser

這個結構突然比之前清楚很多。


上一篇
為什麼 Framework 需要自己的 Response 物件?
下一篇
HTTP Headers 那麼多,Framework 到底怎麼把它們解析出來?
系列文
程式設計沒有告訴你的事:30 天破解每一個 Why18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言