ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Ocelot 集成 GraphQL:通过 DelegatingHandler 在 API 网关内直接执行 GraphQL 查询

Ocelot 集成 GraphQL:通过 DelegatingHandler 在 API 网关内直接执行 GraphQL 查询 API网关后端微服务【免费下载链接】Ocelot.NET API Gateway项目地址https://gitcode.com/gh_mirrors/oc/Ocelot点击查看免费下载本文基于 Ocelot 仓库中的samples/GraphQL示例讲解如何将 GraphQL 查询能力与 .NET API 网关结合在不额外增加一次网络跳转的前提下通过 Ocelot 的 DelegatingHandler 扩展机制在网关内直接执行 GraphQL 查询同时保留 Ocelot 原有的认证、授权、负载均衡等网关能力。读完本文你将掌握如何在 Ocelot 项目中引入 graphql-dotnet 库、编写自定义 DelegatingHandler、注册路由并验证 GET/POST 两种查询方式以及如何将内存示例改造为对接真实 GraphQL 服务。Ocelot 与 GraphQL 的协作定位Ocelot是一个 .NET API 网关负责路由转发、认证授权、限流、负载均衡等网关职责GraphQL是一种查询语言与执行引擎。二者并非互相替代的关系而是可以协同工作。示例项目 README 明确给出了两种推荐协作方式网关前置方案让 Ocelot 位于 GraphQL 服务之前由网关统一处理认证Authentication与授权Authorization等横切关注点无额外跳转方案引入 graphql-dotnet 库将其封装进 Ocelot 的DelegatingHandler中让 Ocelot 网关直接执行 GraphQL 查询避免客户端 → 网关 → GraphQL 服务的额外一次网络往返。本示例项目演示的就是第二种方案代码位于 samples/GraphQL解决方案文件为 OcelotGraphQL.sln。示例项目结构samples/GraphQL/ ├── Models/ │ ├── Hero.cs # GraphQL 类型对应的数据模型 │ └── Query.cs # 查询解析器内存数据源 ├── Properties/ │ └── launchSettings.json ├── GraphQlDelegatingHandler.cs # 核心在网关内执行 GraphQL 查询 ├── Ocelot.Samples.GraphQL.csproj # 项目引用与 NuGet 包 ├── Program.cs # 构建 Schema、注册 Ocelot 与 handler └── ocelot.json # 网关路由配置工程文件 Ocelot.Samples.GraphQL.csproj 显示项目支持net8.0;net9.0;net10.0多目标框架并引用了两个关键 NuGet 包GraphQL8.8.5graphql-dotnet 核心库与GraphQL.NewtonsoftJson8.8.5JSON 序列化同时通过项目引用接入 src/Ocelot.csproj 网关本体。核心实现GraphQlDelegatingHandler整个示例的灵魂是自定义的 DelegatingHandler——GraphQlDelegatingHandler.cs。它继承自System.Net.Http.DelegatingHandler并重写SendAsync方法将进入网关的 HTTP 请求拦截下来直接在进程内交给 graphql-dotnet 执行器处理public class GraphQLDelegatingHandler : DelegatingHandler { private readonly IDocumentExecuter _executer; private readonly IGraphQLTextSerializer _serializer; public GraphQLDelegatingHandler(IDocumentExecuter executer, IGraphQLTextSerializer serializer) { _executer executer; _serializer serializer; } protected override async TaskHttpResponseMessage SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 1. 优先从请求体读取查询文本 var query await request.Content!.ReadAsStringAsync(cancellationToken); // 2. 请求体为空时从 URL 查询字符串中解析例如 ?query... if (query.Length 0) { var decoded WebUtility.UrlDecode(request.RequestUri!.Query); query decoded.Replace(?query, string.Empty); } // 3. 交给 graphql-dotnet 执行器执行 var result await _executer.ExecuteAsync(_ { _.Query query; }); // 4. 将执行结果序列化为 GraphQL 标准响应 var responseBody _serializer.Serialize(result); var media new MediaTypeHeaderValue(application/graphql-responsejson); var response new HttpResponseMessage(HttpStatusCode.OK) { Content new StringContent(responseBody, media), }; // 5. 返回的 HttpResponseMessage 会被 Ocelot 当作普通下游响应处理 return response; } }这段代码的关键点有三处查询来源的双通道兼容既支持 POST 请求体携带查询GraphQL 常见做法也支持 GET 时通过 URL 查询字符串如?query携带查询源码注释也提醒真实生产环境中应更严谨地区分 HTTP 方法与参数来源而不是像示例这样简单替换构造器依赖注入IDocumentExecuter与IGraphQLTextSerializer均来自 DI 容器这正是 Ocelot DelegatingHandler 机制带来的便利——handler 可以注入任何已注册的服务无缝衔接 Ocelot 管线handler 最终返回一个标准的HttpResponseMessage并设置application/graphql-responsejson媒体类型Ocelot 会把它当作一次普通的下游 HTTP 响应原样转发给客户端。启动配置Program.cs 中的三步注册Program.cs 展示了将 GraphQL 接入 Ocelot 的完整引导流程第一步定义 GraphQL Schemavar schema Schema.For( type Hero { id: Int name: String } type Query { hero(id: Int): Hero } , _ { _.Types.IncludeQuery(); });使用 graphql-dotnet 的Schema.For以 SDLSchema Definition Language文本形式声明Hero类型与Query根类型并将 C# 类型Query注册为解析器来源。第二步注册 Schema 并搭建 Ocelotvar builder WebApplication.CreateBuilder(args); builder.Configuration .SetBasePath(builder.Environment.ContentRootPath) .AddOcelot(); // 读取 ocelot.json builder.Services .AddSingletonISchema(schema) // GraphQL Schema 单例 .AddOcelot(builder.Configuration) .AddDelegatingHandlerGraphQLDelegatingHandler(); // 注册路由级 handler注意AddDelegatingHandlerGraphQLDelegatingHandler()没有传global参数默认值为false表示该 handler 只作用于ocelot.json中显式声明的路由Ocelot 官方文档 Delegating Handlers 中对此有完整说明AddDelegatingHandlerT(bool global)的第二个参数默认为false传入true则成为作用于所有路由的全局 handler。第三步启动 Ocelot 中间件管线var app builder.Build(); await app.UseOcelot(); app.Run();路由配置ocelot.json 中的 DelegatingHandlers 声明示例项目的 ocelot.json 配置如下{ Routes: [ { UpstreamPathTemplate: /graphql, DownstreamPathTemplate: /, DownstreamScheme: http, DownstreamHostAndPorts: [ { Host: jsonplaceholder.typicode.com, Port: 80 } ], DelegatingHandlers: [ GraphQLDelegatingHandler ] } ] }UpstreamPathTemplate为/graphql客户端请求http://localhost:5559/graphql时命中该路由DelegatingHandlers数组中的名字必须与 handler 的类名完全一致这里是GraphQLDelegatingHandlerOcelot 才能将其匹配并装配到该路由的 HttpClient 管线上。从 DelegatingHandlerFactory.cs 的实现可以看到 handler 的装配逻辑工厂会先取出全局 handler再根据route.DelegatingHandlers配置按数组顺序排序路由级 handler最后按需追加 Tracing 与 QoS熔断handler。这也解释了为什么本示例的 handler 会先于真正的下游 HTTP 调用执行——它正处于请求发送前的拦截位置因此可以直接吞掉请求并返回 GraphQL 执行结果。运行与请求验证在samples/GraphQL目录下执行dotnet run默认情况下根据 launchSettings.json 中的applicationUrl配置服务监听在http://localhost:5559另有https://localhost:7781的 HTTPS 配置ASPNETCORE_ENVIRONMENT为Development。随后用 Postman、curl 或浏览器即可验证GET 请求GET http://localhost:5000/graphql?query{ hero(id: 4) { id name } }说明README 中给出的地址为localhost:5000结合当前仓库 launchSettings.json 的默认配置实际监听端口为5559HTTP与7781HTTPS请以实际运行端口为准。响应{ data: { hero: { id: 4, name: Tom Pallister } } }POST 请求查询文本放在请求体POST http://localhost:5000/graphql请求体{ hero(id: 4) { id name } }响应与 GET 方式一致{ data: { hero: { id: 4, name: Tom Pallister } } }两条请求路径分别对应GraphQlDelegatingHandler.SendAsync中的两种查询来源分支POST 从request.Content读取GET 从RequestUri.Query解析。数据模型内存查询源返回的数据并非来自真实下游服务而是 Query.cs 中维护的内存列表private readonly ListHero _heroes new() { new Hero { Id 1, Name R2-D2 }, new Hero { Id 2, Name Batman }, new Hero { Id 3, Name Wonder Woman }, new Hero { Id 4, Name Tom Pallister } }; [GraphQLMetadata(hero)] public Hero? GetHero(int id) { return _heroes.FirstOrDefault(x x.Id id); }数据模型 Hero.cs 仅包含Id与Name两个属性Name声明为required。Query类通过[GraphQLMetadata(hero)]特性把GetHero(int id)方法映射为 SDL 中声明的hero(id: Int): Hero字段id与name字段直接对应 Hero 的属性和hero字段的参数/返回值。对接真实 GraphQL 服务README 明确指出示例项目从不访问外部服务数据完全在内存中取得。若要对接真实的 GraphQL 服务器需要把ocelot.json中的路由改为指向你的 GraphQL 端点例如{ Routes: [ { DownstreamPathTemplate: /graphql, DownstreamScheme: http, DownstreamHostAndPorts: [ { Host: yourgraphqlhost.com, Port: 80 } ], UpstreamPathTemplate: /graphql, DelegatingHandlers: [ GraphQlDelegatingHandler ] } ] }将yourgraphqlhost.com与端口替换为实际 GraphQL 服务地址即可。两种部署形态的取舍如下内存模式示例默认handler 直接执行本地 Schema无需下游服务适合演示与开发调试代理模式上述配置handler 拿到查询后转发给真实的 GraphQL 服务执行网关对外仍只暴露/graphql一个统一入口。工作原理补充DelegatingHandler 的执行顺序为了深入理解本示例为何能零跳转执行 GraphQL值得补充 Ocelot 官方文档 Delegating Handlers 中关于执行顺序的说明。一条路由上可以叠加多个 handler执行顺序为全局 handler未出现在路由DelegatingHandlers数组中的部分按注册顺序路由级 handler 以及出现在DelegatingHandlers数组中的全局 handler按数组声明顺序Tracing handler若启用QoS熔断handler若启用HttpClient真正发送HttpRequestMessage。对应到源码 DelegatingHandlerFactory.cs 中的SortByConfigOrder方法其正是按照route.DelegatingHandlers数组的索引位置对 handler 排序。本示例的GraphQLDelegatingHandler在SendAsync内直接返回构造好的HttpResponseMessage而不调用base.SendAsync因此请求根本不会落到第 5 步的 HttpClient 发送环节——这正是网关内执行 GraphQL 的关键机制。注意事项与限制示例中的GraphQlDelegatingHandler对查询来源的处理Replace(?query, ...)是演示性质的简化写法源码注释也明确提示不要在真实世界中这样 hack生产环境应基于 HTTP 方法与参数做严谨解析网关内直接执行 GraphQL 意味着查询执行会占用网关进程的 CPU 与内存高并发场景下需评估性能影响也可考虑将 Schema 与执行器独立部署以分摊负载若将 GraphQL 服务放在 Ocelot 之后网关前置方案则无需自定义 handler按常规方式在ocelot.json中配置路由即可认证、授权等横切能力由 Ocelot 原生中间件承担本示例展示了 Ocelot 与 graphql-dotnet 的一种可行的融合方式实践中请根据团队对网关职责边界的定义选择合适方案。相关资源示例源码samples/GraphQL、解决方案 OcelotGraphQL.sln网关实现src/Ocelot.csproj 与 src/Requester/DelegatingHandlerFactory.cs官方文档Delegating Handlers、GraphQL 示例说明单元测试参考unit/Requester/DelegatingHandlerFactoryTests.cs赞分享API网关后端微服务【免费下载链接】Ocelot.NET API Gateway项目地址https://gitcode.com/gh_mirrors/oc/Ocelot点击查看免费下载相关推荐用 graphql-dotnet 与 DelegatingHandler 为 Ocelot API 网关集成 GraphQL官方示例深度解析用 graphql dotnet 与 DelegatingHandler 为 Ocelot API 网关集成 GraphQL官方示例深度解析 Ocelot 本API网关后端微服务PostGraphile Schema-Only 使用指南在 Node.js 中绕过 HTTP 直接执行 GraphQL 查询PostGraphile Schema Only 使用指南在 Node.js 中绕过 HTTP 直接执行 GraphQL 查询 PostGraphile 的库后端API网关PostGraphile v5 仅 Schema 使用指南在 Node.js 中绕过 HTTP 直接执行 GraphQL 查询PostGraphile v5 仅 Schema 使用指南在 Node.js 中绕过 HTTP 直接执行 GraphQL 查询 本篇技术指南聚焦 PostGr后端API网关上一篇幻兽帕鲁终极存档修复指南3种方法解决跨平台迁移的角色丢失问题下一篇终极流媒体实时翻译工具stream-translator完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表