解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# HttpServer 使用手册
本文档基于æºç `NewLife.Core/Http/HttpServer.cs`,用于说明 `HttpServer`(轻é‡çº§ HTTP æœåŠ¡å™¨ï¼‰çš„èŒè´£ã€è·¯ç”±æ³¨å†Œæ–¹å¼ã€åŒ¹é…规则与使用注æ„事项。
> 关键è¯ï¼šè·¯ç”±æ˜ å°„ã€é€šé…符 `*`ã€å§”托处ç†å™¨ã€æŽ§åˆ¶å™¨æ˜ å°„ã€é™æ€æ–‡ä»¶ã€åŒ¹é…缓å˜ã€çº¿ç¨‹å®‰å…¨ã€‚
---
## 1. 概述
`HttpServer` 继承自 `NetServer` 并实现 `IHttpHost`,用于在 TCP 连接之上æä¾› HTTP å议处ç†èƒ½åŠ›ã€‚
主è¦èŒè´£ï¼š
1. ä¿å˜è·¯ç”±æ˜ å°„ `Routes`ï¼Œå¹¶åœ¨æ”¶åˆ°è¯·æ±‚æ—¶æ ¹æ®è·¯å¾„åŒ¹é… `IHttpHandler`ï¼›
2. 为æ¯ä¸ªç½‘络会è¯åˆ›å»ºå¯¹åº”çš„ `HttpSession` å议处ç†å™¨ï¼ˆ`CreateHandler`);
3. æä¾›å¤šç§ `Map` é‡è½½ï¼ˆå§”托/控制器/陿€æ–‡ä»¶ï¼‰ä»¥ç®€åŒ–注册。
---
## 2. 默认行为与关键属性
### 2.1 基础é…ç½®
æž„é€ å‡½æ•°ä¸ï¼Œ`HttpServer` 的默认é…置为:
- `Name = "Http"`
- `Port = 80`
- `ProtocolType = NetType.Http`
- `ServerName = "NewLife-HttpServer/{Major}.{Minor}"`(从程åºé›†ç‰ˆæœ¬ç”Ÿæˆï¼‰
### 2.2 `ServerName`
- 类型:`String`
- è¯ä¹‰ï¼šç”¨äºŽ HTTP å“应头ä¸çš„ `Server` å称(具体写入由åè®®æ ˆå…¶å®ƒéƒ¨åˆ†å®Œæˆï¼‰ã€‚
### 2.3 `Routes`
- 类型:`IDictionary<String, IHttpHandler>`
- Key:路径(区分大å°å†™è§„则:ä¸åŒºåˆ†ï¼Œ`StringComparer.OrdinalIgnoreCase`)
- Value:处ç†å™¨ï¼ˆ`IHttpHandler`)
说明:
- 路由 Key 会在注册时统一确ä¿ä»¥ `/` 开头。
- åŽæ³¨å†Œä¼šè¦†ç›–先注册(`Routes[path] = handler`)。
---
## 3. 会è¯ä¸Žå议处ç†
### 3.1 `CreateHandler(INetSession session)`
`HttpServer` 会为æ¯ä¸€ä¸ªåº•层网络会è¯åˆ›å»ºä¸€ä¸ªæ–°çš„ `HttpSession`:
- 返回:`new HttpSession()`
è¿™æ„味ç€ï¼š
- HTTP è§£æžã€è¯·æ±‚/å“应生命周期逻辑主è¦ç”± `HttpSession` 承担;
- `HttpServer` æ›´èšç„¦åœ¨â€œè·¯ç”±è¡¨ç»´æŠ¤â€å’Œâ€œåŒ¹é…处ç†å™¨â€ã€‚
### 3.2 异æ¥å¤„ç†å™¨ï¼ˆ`IHttpHandler`)
处ç†å™¨å¥‘约本身是异æ¥çš„:实现接å£åªéœ€ä¸€ä¸ªæ–¹æ³•,返回 `Task`。
```csharp
public interface IHttpHandler
{
Task ProcessRequestAsync(IHttpContext context);
}
```
- **åŒæ¥å®žçް**直接返回 `TaskEx.CompletedTask`(`System.Threading.Tasks` 命å空间,低版本 TFM å¯ç”¨ï¼›ä¸è¦ç”¨ `Task.CompletedTask`,它在 net45 ä¸å˜åœ¨ï¼‰ã€‚
- **异æ¥å®žçް**æ£å¸¸ `await`:数æ®åº“ / 外部 HTTP / 文件 IO ç‰å¼‚æ¥ç‰å¾…直接写在处ç†å™¨é‡Œï¼Œä¸å¿…å†å¥—åŒæ¥æ¡¥ã€‚
- **委托é€ä¼ 异æ¥**:`DelegateHandler` æ”¯æŒ `HttpProcessAsyncDelegate`(`Task (IHttpContext)`)ã€ä»»æ„ç¾åçš„ `async` 方法(返回 `Task`/`Task<T>`ï¼‰ä¸ŽåŒæ¥å§”托 `HttpProcessDelegate`ï¼›`Task<T>` 的结果在任务完æˆåŽå†è¯» `Result`,ä¸å†åŒæ¥é˜»å¡žå–值。
- **ä¸é—´ä»¶æœ¬å°±æ˜¯å¼‚æ¥é“¾**(`HttpMiddlewareDelegate(context, next)`),异æ¥å¤„ç†å™¨å¯ä¸Žä¹‹è‡ªç”±ç»„åˆã€‚
- **当å‰è¾¹ç•Œ**:`HttpSession` ä»åœ¨åŒæ¥å¤„ç†æ¨¡åž‹å†…ç‰å¾…æ•´æ¡ç®¡é“(`ExecutePipeline(...).GetAwaiter().GetResult()`),会è¯çº§å¼‚æ¥æ´¾å‘属下一阶段(需一并解决å“应ä¿åºã€è¯·æ±‚体跨轮所有æƒä¸Žä½Žç‰ˆæœ¬ä¸Šä¸‹æ–‡ä¼ 递)。
- **å‡çº§æç¤º**:`ProcessRequest(IHttpContext)` 已由 `ProcessRequestAsync(IHttpContext)` å–代(返回类型由 `void` 改为 `Task`),既有实现者改å并返回任务å³å¯ï¼Œç¼–译期å¯è§ï¼ˆè§ §11)。
---
## 4. 路由注册 API
`HttpServer` æä¾›å¤šç§è·¯ç”±æ³¨å†Œæ–¹å¼ï¼Œæœ€ç»ˆç»Ÿä¸€èµ°ç§æœ‰æ–¹æ³• `SetRoute(String path, IHttpHandler handler)`。
### 4.1 æ˜ å°„å¤„ç†å™¨å®žä¾‹
```csharp
var server = new HttpServer();
server.Map("/api/test", new MyHandler());
```
- `Map(String path, IHttpHandler handler)`
### 4.2 æ˜ å°„å§”æ‰˜ï¼ˆDelegate)
é€‚ç”¨äºŽå¿«é€Ÿæ³¨å†Œè½»é‡æŽ¥å£ã€‚
- `Map(String path, HttpProcessDelegate handler)`
- `Map<TResult>(String path, Func<TResult> handler)`
- `Map<TModel, TResult>(String path, Func<TModel, TResult> handler)`
- `Map<T1, T2, TResult>(String path, Func<T1, T2, TResult> handler)`
- `Map<T1, T2, T3, TResult>(String path, Func<T1, T2, T3, TResult> handler)`
- `Map<T1, T2, T3, T4, TResult>(String path, Func<T1, T2, T3, T4, TResult> handler)`
说明:
- 这些é‡è½½ä¼šåˆ›å»º `DelegateHandler` 并把委托赋值到 `Callback`。
示例:
```csharp
server.Map("/health", () => "OK");
```
### 4.3 æ˜ å°„æŽ§åˆ¶å™¨
```csharp
server.MapController<MyController>();
```
- `MapController<TController>(String? path = null)`
- `MapController(Type controllerType, String? path = null)`
规则:
- `path` 为空时:默认为 `/{ControllerName}`ï¼Œå…¶ä¸ ControllerName æ¥è‡ª `controllerType.Name.TrimEnd("Controller")`。
- 控制器路由最终会被规范化为:`/{xxx}/*`。
- 注册的处ç†å™¨ç±»åž‹ä¸º `ControllerHandler`,其 `ControllerType` 指å‘ç›®æ ‡æŽ§åˆ¶å™¨ç±»åž‹ã€‚
示例:
```csharp
server.MapController<MyController>("/api");
// 实际注册路由为 /api/*
```
### 4.4 æ˜ å°„é™æ€æ–‡ä»¶ç›®å½•
```csharp
server.MapStaticFiles("/js", "./wwwroot/js");
```
- `MapStaticFiles(String path, String contentPath)`
规则:
- `path` 会确ä¿ä»¥ `/` 开头;
- 实际用于匹é…的路由 Key 为 `path.EnsureEnd("/").EnsureEnd("*")`,例如 `/js/*`ï¼›
- `StaticFilesHandler.Path` 为 `path.EnsureEnd("/")`(例如 `/js/`);
- `StaticFilesHandler.ContentPath` ä¸ºä¼ å…¥çš„ `contentPath`。
---
## 5. 路由设置规范化(`SetRoute`)
所有路由注册最终统一到:
- 傿•°æ ¡éªŒï¼š
- `path` ä¸èƒ½ä¸ºç©º
- `handler` ä¸èƒ½ä¸ºç©º
- 路径规范化:
- `path = path.EnsureStart("/")`
- 覆盖è¯ä¹‰ï¼š
- `Routes[path] = handler`
注æ„:
- `SetRoute` ä¸ä¼šè‡ªåŠ¨è¡¥é½å°¾éƒ¨ `/` 或 `*`,这由 `MapController` / `MapStaticFiles` 负责。
---
## 6. 路由匹é…规则(`MatchHandler`)
`MatchHandler(String path, HttpRequest? request)` ç”¨äºŽæ ¹æ®â€œå·²è§„范化åŽçš„请求路径(ä¸å«æŸ¥è¯¢å—符串)â€åŒ¹é…处ç†å™¨ã€‚
匹é…顺åºï¼š
1. **精确匹é…**:`Routes.TryGetValue(path, out handler)`
2. **缓å˜å‘½ä¸**:
- `_pathCache.TryGetValue(path, out p)`
- ç„¶åŽ `Routes.TryGetValue(p, out handler)`
3. **通é…符匹é…**:枚举 `Routes`ï¼Œå¯¹åŒ…å« `*` çš„ key 执行:
- `key.IsMatch(path)`
### 6.1 通é…符约定
- 仅当路由 key åŒ…å« `*` 时,æ‰è¿›å…¥æ¨¡ç³ŠåŒ¹é…。
- 匹é…逻辑ä¾èµ– `IsMatch` 扩展方法(æ¥è‡ªåŸºç¡€åº“å—符串匹é…能力)。
### 6.2 匹é…ç¼“å˜ `_pathCache`
- 类型:`IDictionary<String, String>`
- Key:请求路径 `path`
- Value:命ä¸çš„路由 key(例如 `/api/*`)
缓å˜ç–略:
- å‘½ä¸ `StaticFilesHandler`:缓å˜è¯¥ `path -> routeKey`。
- éžé™æ€æ–‡ä»¶ï¼šä»…当 `path.Split('/')` 段数 `<= 3` æ‰ç¼“å˜ã€‚
目的:
- é¿å…åŠ¨æ€ URL(例如带多段 id çš„è·¯å¾„ï¼‰é€ æˆç¼“å˜æ— é™è†¨èƒ€ï¼›
- 对常è§çŸè·¯å¾„åŠ é€Ÿæ¨¡ç³ŠåŒ¹é…。
缓å˜å®žçŽ°ä¸Žä¸Šé™ï¼š
- `_pathCache` 为并å‘å—典,写入走 `MaxPathCacheSize`(默认 4096)é™å®¹ï¼›è¾¾åˆ°ä¸Šé™åŽé™é»˜ä¸å†™ï¼Œè¯·æ±‚èµ°æ£å¸¸è·¯ç”±åŒ¹é…。
---
## 7. çº¿ç¨‹å®‰å…¨ä¸Žå¹¶å‘æ³¨æ„事项
当å‰å®žçŽ°çš„å¹¶å‘è¯ä¹‰ï¼š
- `Routes` 默认是 `Dictionary`,并éžå¹¶å‘容器;
- 典型场景:å¯åŠ¨é˜¶æ®µé›†ä¸æ³¨å†Œè·¯ç”±ï¼Œè¿è¡ŒæœŸåªè¯»è®¿é—®ï¼›
- è‹¥è¿è¡ŒæœŸåЍæ€å¢žåˆ 路由:需è¦è°ƒç”¨æ–¹è‡ªè¡ŒåŠ é”åºåˆ—化访问。
风险点:
- 在è¿è¡ŒæœŸä¿®æ”¹ `Routes` å¹¶åŒæ—¶è°ƒç”¨ `MatchHandler`,å¯èƒ½è§¦å‘ `Dictionary` 枚举异常或产生ä¸ä¸€è‡´ç»“果。
- `_pathCache` 为并å‘å—典且写入é™å®¹ï¼ˆ`MaxPathCacheSize`),并å‘读写安全。
建议:
- å¯åŠ¨å®ŒæˆåŽä¸è¦å†å˜æ›´è·¯ç”±ï¼›
- æˆ–è€…åœ¨å¤–éƒ¨åŠ é”ï¼Œç¡®ä¿ `Map/SetRoute` 与 `MatchHandler` ä¸å¹¶å‘执行。
---
## 8. 最å°ç¤ºä¾‹
> è¯´æ˜Žï¼šç¤ºä¾‹åªæ¼”示 `HttpServer` 的路由注册与组åˆã€‚实际å¯åŠ¨ç›‘å¬ã€ä¼šè¯æ”¶å‘ç‰èƒ½åŠ›ç”± `NetServer` æä¾›ï¼Œè¯·ä»¥é¡¹ç›®å†…现有示例或 `NetServer` 文档为准。
```csharp
using NewLife.Http;
var server = new HttpServer
{
Port = 8080,
ServerName = "MyServer/1.0",
};
server.Map("/health", () => "OK");
server.MapStaticFiles("/static", "./wwwroot");
server.MapController<MyController>("/api");
server.Start();
```
---
## 9. 常è§é—®é¢˜
### 9.1 为什么 `MapController` ä¼šè‡ªåŠ¨åŠ ä¸Š `/*`?
控制器通常需è¦åŒ¹é…其“å路径â€ï¼Œä¾‹å¦‚ `/api/user/list`ã€`/api/user/detail/123` ç‰ã€‚通过 `/*` 让åŒä¸€ä¸ªæŽ§åˆ¶å™¨å¤„ç†å™¨æŽ¥ç®¡è¯¥å‰ç¼€ä¸‹çš„æ‰€æœ‰è¯·æ±‚。
### 9.2 为什么部分路径ä¸åšç¼“å˜ï¼Ÿ
å¯¹å¤šæ®µåŠ¨æ€ URL å…¨é‡ç¼“å˜å¯èƒ½å¯¼è‡´ `_pathCache` æŒç»å¢žé•¿ï¼ˆç¼“å˜è†¨èƒ€ï¼‰ã€‚当å‰ç–略仅缓å˜çŸè·¯å¾„æˆ–é™æ€æ–‡ä»¶å‘½ä¸ï¼Œä»¥å®žçŽ°åŠ é€Ÿä¸Žç©ºé—´ä¹‹é—´çš„æŠ˜ä¸ã€‚
---
## 10. 相关æºç
- `NewLife.Core/Http/HttpServer.cs`
- `NewLife.Core/Http/HttpSession.cs`
- `NewLife.Core/Http/Handlers/DelegateHandler.cs`(按项目实际路径为准)
- `NewLife.Core/Http/Handlers/ControllerHandler.cs`
- `NewLife.Core/Http/Handlers/StaticFilesHandler.cs`
---
## 11. v12 è¡Œä¸ºå˜æ›´ï¼ˆå‡çº§é¡»çŸ¥ï¼‰
| å˜æ›´ | 说明 |
|------|------|
| è·¯ç”±å‚æ•°ä¼˜å…ˆ | è·¯å¾„å‚æ•°ï¼ˆ`{id}` ç‰ï¼‰åœ¨ `PrepareRequest` è§£æžå®Œä¹‹åŽåˆå¹¶ï¼Œå› æ¤**优先于**查询串/表å•åŒåå—æ®µã€‚æ¤å‰ `?id=999` å¯è¦†ç›–è·¯å¾„å‚æ•°ï¼ŒçŽ°åœ¨ä»¥è·¯å¾„å‚æ•°ä¸ºå‡† |
| å“应体所有æƒéšå“应 | 处ç†å™¨è¿”å›žåŽ `HttpResponse.Body` ä¼šè¢«å½’è¿˜æ± åŒ–ç¼“å†²ã€‚å¤„ç†å™¨è‹¥åœ¨è¿”回åŽä»æŒæœ‰è¯¥å¼•用(审计ã€é‡è¯•ã€æ—¥å¿—),需先自行å¤åˆ¶ |
| `Content-Length` 规则 | 204/304 å“应ä¸å†å£°æ˜Ž `Content-Length`ï¼›`Build` 与 `BuildHeaderPacket`(HEAD/æµå¼ï¼‰ä¸¤æ¡è·¯å¾„åŒè§„则(RFC 7230 §3.3.2) |
| `Transfer-Encoding: chunked` 请求 | **已支æŒåˆ†å—è§£ç **(å«å—扩展 `;` 与终æ¢å—åŽçš„ trailer 段):跨轮累积原始å—节,收é½ç»ˆæ¢å—åŽè§£ç äº¤ä»˜ï¼Œå¹¶åŒæ¥ç§»é™¤ `Transfer-Encoding` 头ã€è¡¥é½ `Content-Length`——业务侧看到的主体与头部,与普通 `Content-Length` 请求完全一致。åå—长度行回 `400`,原始å—节超 `MaxRequestLength` 回 `413`ï¼Œå…¶å®ƒä¼ è¾“ç¼–ç (gzip/identity)ä»å›ž `400` |
| 路径缓å˜å¹¶å‘化 | `_pathCache` 改为并å‘å—典并新增 `MaxPathCacheSize`(默认 4096),超é™åŽé™é»˜ä¸å†™ |
| WebSocket æ¡æ‰‹æ ¡éªŒ | 带 `Sec-WebSocket-Key` ä½†æ¡æ‰‹å››è¦ç´ ä¸åˆæ³•时回 `400 Bad Request`(æ¤å‰ä¼šè½åˆ°æ™®é€šè·¯ç”±æŠ¥ 404ï¼ŒæŽ©ç›–çœŸå®žåŽŸå› ï¼‰ï¼›`Upgrade`/`Connection`/`Sec-WebSocket-Version` 按逗å·åˆ†éš”çš„**令牌列表**è§£æžï¼Œç‰ˆæœ¬å« `13` å³æŽ¥å— |
| éžæ³• `Content-Length` | `Content-Length` éžæ•°å—ã€è´Ÿæ•°æˆ–超出范围时回 `400 Bad Request` å¹¶å…³é—连接;æ¤å‰è§£æžå¤±è´¥é™é»˜å½“ 0 处ç†ï¼Œè¯·æ±‚体边界éšä¹‹é”™ä½ |
| HEAD ä¸å‘实体 | HEAD 请求åªå‘é€å“应头,实体æµï¼ˆé™æ€æ–‡ä»¶/嵌入资æºï¼‰å°±åœ°é‡Šæ”¾ï¼›æ¤å‰ä¼šæŠŠæ•´ä¸ªå®žä½“å‘出去 |
| 畸形/è¶…é™è¯·æ±‚头ä¸å†é™é»˜ä¸¢å¼ƒ | æ— ç©ºè¡Œä¸”å‰10å—èŠ‚å†…æ— ç©ºæ ¼ï¼ˆä¸åƒ HTTP 开头)或å•包超出 `MaxHeadLength`(新增å¯é…置属性,默认 64KB)的å—节,æ¤å‰ç›´æŽ¥ä¸¢å¼ƒâ€”—既ä¸å›žå“应也ä¸å…³è¿žæŽ¥ï¼Œä¸”ä¸è¿›ç¼“å˜ä½¿åœæ»žåˆ¤å®šå¤±æ•ˆï¼Œå¯¹ç«¯å¯é›¶æˆæœ¬å ä½è¿žæŽ¥åé¢ã€‚çŽ°æŒ‰æ— æ•ˆè¯·æ±‚å›ž `400` å¹¶å…³é—ï¼›ä¸è¶³10å—节还判æ–ä¸å‡ºæ˜¯å¦ä¸º HTTP 开头的残缺å‰ç¼€æ”¹ä¸ºå…¥ç¼“å˜ç‰å¾…åŽç»åˆ†ç‰‡ï¼ˆæ¤å‰åŒæ ·è¢«ä¸¢å¼ƒï¼Œä½¿åŽç»è¡¥é½çš„åˆæ³•请求与残缺å‰ç¼€ä¹‹é—´å¤±æ¥ï¼‰ |
| è·¯ç”±å‚æ•°åªæ¥è‡ªå‘½ä¸è·¯ç”± | å€™é€‰è·¯ç”±çš„å‚æ•°æ¤å‰å…ˆå†™è¿›è¯·æ±‚傿•°å—å…¸ã€å¤±è´¥æ—¶ä¹Ÿä¸å›žæ”¶ï¼Œå‘½ä¸è·¯ç”±çš„傿•°åˆä¸Žå®ƒä»¬æ··åœ¨ä¸€èµ·ï¼Œå¯¼è‡´å¤±è´¥å€™é€‰ï¼ˆå¦‚ `/api/{id}/detail` 对 `/api/5/info`)残留的 `id=5` 覆盖åŒåæŸ¥è¯¢å‚æ•° `?id=999`ã€‚çŽ°æ”¹ä¸ºå€™é€‰å‚æ•°å…ˆåœ¨ä¸´æ—¶å—å…¸è¯•ç®—ï¼ŒåŒ¹é…æˆåŠŸæ‰åˆå¹¶â€”â€”ã€Œè·¯å¾„å‚æ•°ä¼˜å…ˆäºŽæŸ¥è¯¢ä¸²ã€åªå¯¹çœŸæ£å‘½ä¸çš„路由æˆç«‹ |
| æ— é•¿åº¦è¯·æ±‚å³æ— 实体 | è¯·æ±‚æ—¢æ— `Content-Length` ä¹Ÿæ— `Transfer-Encoding` 时按 RFC 9112 §6.3 视为**没有实体**(`Body` 为 null),头部之åŽçš„å—节属于åŒè¿žæŽ¥çš„下一个请求(HTTP/1.1 æµæ°´çº¿ï¼‰ã€‚æ¤å‰ä¸€å¾‹æŒ‰å®žä½“åžæŽ‰ï¼šä¸€æ¬¡å‘å‡ºä¸¤æ¡æ™®é€š GET 时第二个请求被é™é»˜ä¸¢å¼ƒâ€”—既ä¸å›žå“应也ä¸å…³è¿žæŽ¥ï¼Œå®¢æˆ·ç«¯åªèƒ½ç‰è‡ªèº«è¶…时。例外:HTTP/1.0 çš„ POST/PUT/PATCH ä¿ç•™â€œè¯»åˆ°çš„都是实体â€çš„è€å¼å…¼å®¹è¯ä¹‰ï¼ˆè€å®¢æˆ·ç«¯ä»¥è¿žæŽ¥å…³é—定界实体ã€ä¸å‘ CL) |
| 状æ€è¡Œä¸Žå¤´éƒ¨æ¸…æ´— | å“åº”çŠ¶æ€æè¿°ï¼ˆå«å¼‚常消æ¯ï¼‰ä¸Žå¤´éƒ¨å—段å/值统一剔除 CR/LF ç‰æŽ§åˆ¶å—符(CR/LF 压æˆç©ºæ ¼ï¼‰ã€‚æ¤å‰å¼‚常消æ¯å¤¹å¸¦æ¢è¡Œæ—¶ï¼ŒæŽ¥æ”¶æ–¹ä¼šæŠŠå®ƒè§£æžæˆé¢å¤–çš„å“应头(å“应拆分),å¯ç”¨æ¥ä¼ªé€ `Content-Length`/`Location`/`Set-Cookie`;业务自定义头部里的 TAB 与å¯è§å—符ä¸å—å½±å“ |
| å‡çº§åŽåŒè½®å—节交 WebSocket | 处ç†å®Œå‡çº§è¯·æ±‚åŽï¼Œå¤´éƒ¨ä¹‹åŽåˆ°è¾¾çš„å—èŠ‚ï¼ˆåŒæ®µ/åŒè½®çš„ WS 帧)直接交给 `WebSocket.Process` è§£æžï¼Œä¸å†ä¸¢å¼ƒã€‚RFC 6455 §4.1 å…许客户端å‘å®Œæ¡æ‰‹å°±å‘帧,æ¤å‰é¦–帧会é™é»˜æ¶ˆå¤±ï¼ˆ"è¿žä¸Šäº†ä½†ç¬¬ä¸€æ¡æ¶ˆæ¯æ²¡åˆ°");分å—请求体之åŽçš„剩余å—èŠ‚åŒæ ·å¤„ç† |
| WS å…³é—ç æŒ‰ RFC å£å¾„区分 | å议错误回 **1002**ã€æ–‡æœ¬è´Ÿè½½éžæ³•ï¼ˆéž UTF-8)回 **1007**ã€å•帧或分片消æ¯è¶…上é™å›ž **1009**(æ¤å‰ä¸€å¾‹æŒ‰ 1002);å•帧上é™å¯¹"整帧一次到达"çš„å¸§åŒæ ·ç”Ÿæ•ˆï¼ˆæ¤å‰åªçº¦æŸæœªå®Œæ•´å¸§ï¼Œå¯¹ç«¯æŠŠè¶…é™å¸§åŽ‹åœ¨ä¸€è½®é‡Œå³å¯ç»•过) |
| `DefaultHttpContext.Current` éšå¼‚æ¥ä¸Šä¸‹æ–‡æµåЍ | ç”± `[ThreadStatic]` 改为 `AsyncLocal`(net45 é™çº§ä¸ºçº¿ç¨‹æœ¬åœ°ï¼‰ï¼šä¸é—´ä»¶ `await` 之åŽã€å¤„ç†å™¨å†… `Task.Run` 派生的任务里都能å–到本请求上下文,æ¤å‰ä¼šå–到 null(åŒä¸€è¯·æ±‚内"éšæœº"丢失) |
| 路由åŒç±»å†…æŒ‰å…·ä½“åº¦æŽ’åº + è·¯å¾„å‚æ•°è§£ç | 傿•°åŒ–路由之间按"å—颿®µæ•°é‡ä»Žå¤šåˆ°å°‘"排åºï¼ˆåŒå…·ä½“åº¦ä¿æŒæ³¨å†Œé¡ºåºï¼‰ï¼Œå…ˆæ³¨å†Œçš„宽泛模å¼ï¼ˆå¦‚ `/api/{controller}/{id}`)ä¸å†é®ä½åŽæ³¨å†Œçš„具体模å¼ï¼›è·¯å¾„傿•°ä¸Žé€šé…傿•°æŒ‰ç™¾åˆ†å·è§£ç (与查询串å£å¾„一致),通é…傿•°é€æ®µè§£ç åŽä»¥ `/` 连接 |
| 处ç†å™¨å¥‘约异æ¥åŒ– | `IHttpHandler.ProcessRequest(IHttpContext)`(`void`)改为 `ProcessRequestAsync(IHttpContext)`(`Task`)。处ç†å™¨å®žçŽ°è€…æ”¹å + 返回 `TaskEx.CompletedTask`ï¼ˆåŒæ¥å®žçŽ°ï¼‰æˆ– `await`(异æ¥å®žçŽ°ï¼‰å³å¯ï¼›`DelegateHandler`ã€`ControllerHandler`ã€`MethodFilterHandler` 与ä¸é—´ä»¶ç®¡é“凿Œ‰å¼‚æ¥è¡”æŽ¥ï¼Œå¼‚æ¥æ“作方法的结果在任务完æˆåŽå†å– |
---
## 12. 能力边界(ä¸åšä»€ä¹ˆï¼‰
`HttpServer` çš„å®šä½æ˜¯**è½»é‡çº§åµŒå…¥å¼ HTTP æœåС噍**ï¼Œä¸æ˜¯é€šç”¨ Web æœåŠ¡å™¨ã€‚æ–°å¢žèƒ½åŠ›åªæŒ‰ä¸¤æ¡åˆ¤æ®å–èˆâ€”—**下游能æ£å¸¸ç”¨**ã€**ä¸åœ¨è¿žæŽ¥è¾¹ç•Œä¸Šåˆ¶é€ æ§ä¹‰**;其余需求请直接用 ASP.NET Core / Kestrel。
**明确ä¸åš**:
| ä¸åšé¡¹ | æ›¿ä»£æ–¹å¼ |
|--------|----------|
| HTTP/2ã€HTTP/3 | Kestrel |
| HTTP 层 TLS/SNI | `NetServer` çš„ SSL é…置,或å‰ç½®åå‘ä»£ç† |
| å“应压缩(gzip/deflate) | å‰ç½®åå‘ä»£ç† |
| `Range` 分段 / `206` | æ–点ç»ä¼ 属下载器能力(`WebClientX`),ä¸å±žæœåŠ¡ç«¯ |
| `ETag` / `Last-Modified` / æ¡ä»¶è¯·æ±‚ | æ— ç¼“å˜è¯ä¹‰ï¼Œé™æ€æ–‡ä»¶é å‰ç½®åå‘ä»£ç† |
| 请求级慢速攻击专项防护 | 已由 `HttpSession.MaxRequestIdle`ï¼ˆè¯·æ±‚åœæ»žä¸Šé™ï¼Œé»˜è®¤ 30 秒回 408)与网络层会è¯è¶…时兜底 |
**`MapController` å°è¾¹**ï¼šåªæä¾›â€œæœ€å° MVCâ€â€”â€”æŒ‰è·¯å¾„æ®µå®šä½æ–¹æ³•ã€ä»Žå‚æ•°å—典绑å‚ã€è°ƒ DIã€è¿”回 `HttpResponse`。以下一律ä¸åšï¼Œé¿å…è†¨èƒ€ä¸ºæ ‡å‡† MVC:
- 特性路由(`[Route]`/`[HttpGet]` ç‰ï¼‰
- 模型绑定验è¯ä¸Ž `ModelState`
- 过滤器管é“(`IActionFilter` 那一套)
- 内容åå•†ä¸Žæ ¼å¼åŒ–器
**已知é™åˆ¶**(明确接å—,ä¸ä¿®ï¼‰ï¼š
- `Transfer-Encoding` 与 `Content-Length` åŒæ—¶å‡ºçŽ°æ—¶ä¸æ‹’ç»ï¼ŒæŒ‰ chunked 处ç†ä¸”ä¸å¼ºåˆ¶å…³é—连接。åªåœ¨**与å‰ç½®ä»£ç†å…±å˜**æ—¶æž„æˆè¯·æ±‚èµ°ç§é¢ï¼›å†…网/åµŒå…¥å¼æ— 代ç†éƒ¨ç½²ä¸æˆç«‹ï¼Œæ”¶ç´§ä¸Žè½»é‡å–å‘相悖。
- HTTP/1.0 请求æºå¸¦ `Transfer-Encoding` ä¸åšç‰¹æ®Šå¤„ç†ã€‚HTTP/1.0 客户端ä¸ä¼šäº§ç”Ÿè¯¥ç»„åˆï¼Œå®žé™…ä¸å¯è¾¾ã€‚
- 分å—请求体的原始å—节全é‡ç¼“å†²ï¼ˆå— `MaxRequestLength` 约æŸï¼‰ï¼Œä¸åšæµå¼äº¤ä»˜ï¼›ä¸šåŠ¡ä¾§å§‹ç»ˆæ‹¿åˆ°å®Œæ•´ä¸»ä½“ã€‚
- `Expect` åªå¤„ç† `100-continue`,其余期望一律忽略(ä¸å›ž `417`)。
|