目錄
- 引入
- 組合模式
- 原始碼
引入
在上一篇執行 _connectionDelegate 之後,HttpConnectionMiddleware 處理請求
return connection.ProcessRequestsAsync(_application);
在 HttpConnection 中呼叫 IRequestProcessor 的 ProcessRequestsAsync 方法
await requestProcessor.ProcessRequestsAsync(httpApplication);
跳轉到 IRequestProcessor 的實現類 HttpProtocol 的 ProcessRequests 方法
private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) where TContext : notnull
這裡會建立 MessageBody
var messageBody = CreateMessageBody();
然後建立一個真正的 context,這個時候 context 就被轉換成一個可讀的 HTTPContext
var context = application.CreateContext(this);
接著開始真正的呼叫 HTTPApplication,走到 Host 裡面,接著執行 startup 裡面寫的管道
// Run the application code for this request
await application.ProcessRequestAsync(context);
那麼接下來的 controller,api 如何出來呢?
通過 routing 和 endpoints,每個請求會 map 到一個 endpoint
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
呼叫 UseRouting 之後會新增一個 EndpointRoutingMiddleware,用於匹配路由,會將一個 URL 匹配到一個 Endpoint
MapControllers 會掃描所有 api 上面的路由,新增到 DataSource 中,它被 EndpointDataSource 所使用
由於 DataSource 的存在,可以找到匹配,匹配之後會將 SelectEndpoint 掛到 HttpContext
而 Endpoint 中是一個 RequestDelegate
如果不使用 Route 和 Endpoint,可以使用這樣的形式
app.Run(async context => { await context.Response.WriteAsync("aaa"); });
在匹配的時候我們用到了組合的設計模式
組合模式
將物件組合成樹形結構以表示“部分-整體”的層次結構,使得使用者對單個物件和組合物件的使用具有一致性
組合模式(Composite)經常用於樹形結構,為了簡化程式碼,使用Composite可以把一個葉子節點與一個父節點統一起來處理
Route DfaNode:通過遍歷的形式,當一個 url 進來的時候,會把所有的路由進行分割,從上到下進行匹配
原始碼
https://github.com/dotnet/aspnetcore/
在目錄 Microsoft.AspNetCore.Routing.Matching 下面有一個 DfaMatcher,它繼承自 Matcher
internal sealed partial class DfaMatcher : Matcher
DfaMatcher 有一個 MatchAsync 方法
public sealed override Task MatchAsync(HttpContext httpContext)
在 MatchAsync 方法裡面首先拿到 path,接著查詢候選集
var path = httpContext.Request.Path.Value!;
var (candidates, policies) = FindCandidateSet(httpContext, path, segments);
FindCandidateSet 方法裡面有已經構造好的 DfaState,包含了路由分割資訊
private readonly DfaState[] _states;
在進行 Match 之前需要有一個 DfaTree,可以在 DfaMatcherBuilder 中找到
DfaMatcherBuilder 有一個 Build 方法
public override Matcher Build()
在 Build 方法裡面 BuildDfaTree
var root = BuildDfaTree(includeLabel);
BuildDfaTree 由很多個 Node 組成
AddNode(root, states, exitDestination);
然後構建 DfaState
states[exitDestination] = new DfaState(
Array.Empty<Candidate>(),
Array.Empty<IEndpointSelectorPolicy>(),
JumpTableBuilder.Build(exitDestination, exitDestination, null),
null);
再把 DfaState 傳給 DfaMatcher
return new DfaMatcher(_loggerFactory.CreateLogger<DfaMatcher>(), _selector, states, maxSegmentCount);
由於這個過程比較複雜,所以將這個過程包裝在 DataSourceDependentMatcher,但是它不是一個 Matcher
DataSourceDependentMatcher 的 MatchAsync 方法直接呼叫了 CurrentMatcher 的 MatchAsync 方法
public override Task MatchAsync(HttpContext httpContext)
{
return CurrentMatcher.MatchAsync(httpContext);
}
所以 DataSourceDependentMatcher 的主要功能是構造一個 Matcher,就是一個 DfaMatcher
private Matcher CreateMatcher(IReadOnlyList<Endpoint> endpoints)
對外部來講只是一個 Matcher,然後它需要實現對內部的封裝,把所有細節隱藏在 DataSourceDependentMatcher 中
DataSourceDependentMatcher 只是一個對 DfaMatcher 葉子節點的組合
課程連結
https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含連結: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。
如有任何疑問,請與我聯絡 (MingsonZheng@outlook.com) 。