概述
HTTP 网关是 silky 框架对外暴露 REST 接口的入口层。当外部请求到达网关时,请求会依次经过 ASP.NET Core 路由匹配 → 参数解析 → 服务条目执行 → 响应包装 四个阶段,最终将 RPC 执行结果以统一 JSON 格式返回。
本文聚焦于运行时请求管道的源码实现,说明每个阶段的核心组件与数据流转。
整体管道结构
外部 HTTP 请求
│
▼
WrapperResponseMiddleware(拦截 Response.Body,准备统一包装)
│
▼
ASP.NET Core 路由匹配(SilkyServiceEntryEndpointDataSource)
│ ——→ 匹配失败:404
▼
MessageReceivedHandlerBase.Handle()
│ 1. 创建 HttpContextServerCallContext
│ 2. 初始化(设置超时令牌、上报活动追踪、解析用户声明)
▼
DefaultHttpMessageReceivedHandler.HandleCallAsyncCore()
│ 1. 解析 HTTP 参数 → object[]
│ 2. 解析 ServiceKey
│ 3. 启动 Monitor(可选)
│ 4. 调用 IHttpExecutor.Execute()
│ 5. 写入响应正文
▼
WrapperResponseMiddleware
│ 1. 读取 Response.Body 内容
│ 2. 包装为 { status, code, result / errorMessage }
│ 3. 写回响应
▼
客户端收到统一格式 JSON
阶段一:路由匹配
网关启动时,SilkyServiceEntryEndpointDataSource 将所有 ServiceEntry 注册为 ASP.NET Core Endpoint。路由匹配由标准 ASP.NET Core 路由机制完成,匹配成功后将 ServiceEntry 和 ServiceEntryDescriptor 写入 HttpContext.Items,供后续组件读取。
// SilkyHttpCoreCollectionExtensions.cs
services.AddSingleton<SilkyServiceEntryEndpointDataSource>();
services.AddSingleton<ServiceEntryEndpointFactory>();
services.AddSingleton<SilkyServiceEntryDescriptorEndpointDataSource>();
阶段二:消息接收处理器
路由匹配成功后,请求进入 DefaultHttpMessageReceivedHandler(IMessageReceivedHandler 的默认实现)。
MessageReceivedHandlerBase.Handle()
基类负责统一的初始化流程:
public virtual Task Handle(ServiceEntry serviceEntry, HttpContext httpContext)
{
// 1. 构造 HttpContextServerCallContext(绑定超时令牌、活动追踪)
var serverCallContext = new HttpContextServerCallContext(
httpContext, serviceEntry.ServiceEntryDescriptor,
_serializer, GatewayOptions, Logger);
// 2. 初始化:设置 CancellationToken、写入追踪标签
serverCallContext.Initialize();
httpContext.RequestAborted = serverCallContext.CancellationToken;
// 3. 派发给子类实现
return HandleCallAsyncCore(httpContext, serverCallContext, serviceEntry);
}
HttpContextServerCallContext.Initialize() 内部会:
- 获取当前 Activity 并写入
serviceentry.id标签 - 调用
SilkyRpcEventSource.Log.CallStart()上报指标 - 解析请求头中的用户声明(
SetUserClaims)
DefaultHttpMessageReceivedHandler.HandleCallAsyncCore()
子类完成参数解析、执行和结果写入:
protected override async Task HandleCallAsyncCore(
HttpContext httpContext,
HttpContextServerCallContext serverCallContext,
ServiceEntry serviceEntry)
{
// 1. 解析 HTTP 参数
var parameters = await _parameterParser.Parser(httpContext.Request, serviceEntry);
// 2. 将请求参数写入 RpcContext(用于审计)
RpcContext.Context.SetRequestParameters(_auditSerializer.Serialize(parameters));
// 3. 解析 ServiceKey(来自请求头 serviceKey)
var serviceKey = ResolveServiceKey(httpContext);
if (!serviceKey.IsNullOrEmpty())
RpcContext.Context.SetServiceKey(serviceKey);
// 4. 可选:启动调用监控
if (rpcOption.EnableMonitor)
serverHandleInfo = serverHandleMonitor?.Monitor(...);
// 5. 执行服务条目(本地或远程)
var executeResult = await _executor.Execute(serviceEntry, parameters, serviceKey);
// 6. 写入响应正文(JSON 序列化或 IActionResult 执行)
await serverCallContext.HttpContext.Response.WriteAsync(responseData);
}
阶段三:HTTP 参数解析
DefaultHttpRequestParameterParser 负责从 HttpRequest 各数据源提取参数,构造 object[] 传递给执行器。
参数来源映射
ParameterFrom | 数据来源 | 说明 |
|---|---|---|
Form | request.Form | multipart/form-data 表单数据及文件上传 |
Query | request.Query | URL 查询字符串 |
Header | request.Headers | 请求头,同时写入 RpcContext 附件 |
Body | request.Body | JSON 请求体(非 GET 请求) |
Path | 路由参数 | 通过 Router.ParserRouteParameters() 解析路由段 |
参数解析流程
public async Task<object[]> Parser(HttpRequest httpRequest, ServiceEntry serviceEntry)
{
// 1. 收集各来源原始数据
var requestParameters = await ParserHttpRequest(httpRequest, serviceEntry);
// 2. 委托给 HttpParameterResolver,将原始数据按 RpcParameter 定义绑定为方法参数列表
var httpParameterResolver =
EngineContext.Current.ResolveNamed<IParameterResolver>(ParameterType.Http.ToString());
return httpParameterResolver.Parser(serviceEntry, requestParameters, httpRequest.HttpContext);
}
HttpParameterResolver 根据 ServiceEntry.Parameters(由启动时生成的 RpcParameter[])将各来源数据映射到对应的方法参数位置。
阶段四:HTTP 执行器
DefaultHttpExecutor 是网关专用的执行器适配器,将网关的 HttpRequest 路径桥接到通用的 IExecutor:
public class DefaultHttpExecutor : IHttpExecutor
{
private readonly IExecutor _executor;
public async Task<object> Execute(ServiceEntry serviceEntry, object[] parameters, string serviceKey = null)
=> await _executor.Execute(serviceEntry, parameters, serviceKey);
}
IExecutor(DefaultExecutor)随后根据 ServiceEntry.IsLocal 决定走本地执行还是远程 RPC 调用,详见 executor 文档。
阶段五:响应包装中间件
WrapperResponseMiddleware 拦截 Response.Body,在所有中间件执行完成后,将原始响应正文包装为统一格式。
直通场景(不包装)
以下情况 WrapperResponseMiddleware 会直通,不做包装:
- 请求路径匹配
GatewayOptions.IgnoreWrapperPathPatterns中的正则(且为 GET 请求) ServiceEntry.ReturnType是IActionResult的子类ServiceEntryDescriptor.IsUnWrapperResult()返回true(通常通过[UnWrapperResult]特性标注)
包装格式
private async Task HandleResponseAsync(HttpContext context, string body, int httpStatusCode)
{
var status = context.Response.GetResultCode(httpStatusCode);
var responseResultDto = new ResponseResultDto
{
Status = (int)status,
Code = status.ToString(),
Result = _serializer.Deserialize<dynamic>(body), // 成功时
};
// 失败时填充 ErrorMessage
var jsonString = _serializer.Serialize(responseResultDto);
context.Response.StatusCode = (int)status;
await context.Response.WriteAsync(jsonString);
}
成功响应格式:
{
"status": 200,
"code": "Success",
"result": { /* 业务数据 */ }
}
失败响应格式:
{
"status": 500,
"code": "ServerError",
"errorMessage": "具体错误描述"
}
核心组件汇总
| 组件 | 类型 | 职责 |
|---|---|---|
SilkyServiceEntryEndpointDataSource | Singleton | 将 ServiceEntry 注册为 ASP.NET Core Endpoint |
MessageReceivedHandlerBase | Scoped(抽象) | 构造 HttpContextServerCallContext,初始化追踪和超时 |
DefaultHttpMessageReceivedHandler | Scoped | 参数解析、执行、结果写入 |
DefaultHttpRequestParameterParser | Scoped | 从 Form/Query/Header/Body/Path 解析方法参数 |
DefaultHttpExecutor | Transient | 桥接 HTTP 请求到 IExecutor |
WrapperResponseMiddleware | Middleware | 拦截并包装为统一响应 JSON |
HttpContextServerCallContext | Per-request | 持有请求上下文、超时令牌、追踪活动 |
