尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Apache APISIX degraphql 插件实战:用 RESTful 接口代理 GraphQL 查询(配置、源码与测试全解析)

Apache APISIX degraphql 插件实战:用 RESTful 接口代理 GraphQL 查询(配置、源码与测试全解析) Apache APISIX degraphql 插件实战用 RESTful 接口代理 GraphQL 查询配置、源码与测试全解析【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisixdegraphql是 Apache APISIX 提供的 GraphQL 适配插件它允许你把一段 GraphQL 查询预先固化在路由配置中然后通过普通的 RESTful 请求POST/GET触发该查询并由网关自动完成REST 参数 → GraphQL 变量的转译后转发给上游 GraphQL 服务。本文以官方文档 docs/en/latest/plugins/degraphql.md 为主线结合插件源码 apisix/plugins/degraphql.lua 与测试用例 t/plugin/degraphql.t带你掌握该插件的完整配置方法、请求转换原理与边界行为可直接在真实网关中落地使用。插件概述RESTful API 与 GraphQL 之间的转译器在微服务架构中前端往往希望以最简洁的 REST 语义调用后端能力而后端团队可能已经采用 GraphQL 暴露数据模型。degraphql插件解决的正是这个最后一公里问题将客户端的 RESTful 请求解码decode为上游 GraphQL 服务能够理解的查询。其工作方式非常直观网关管理员把一段 GraphQL 查询query写入路由上的插件配置客户端向该路由发起 POST 或 GET 请求携带与查询中变量同名的参数插件在access阶段把请求体/查询串中的参数提取出来组装成标准的 GraphQL 载荷queryoperationNamevariables网关将组装后的载荷转发给上游 GraphQL 服务器返回结果原样透传给客户端。从源码看该插件位于 apisix/plugins/degraphql.lua版本0.1执行优先级priority 509在网关插件链中属于中等偏高优先级确保在其他改写类插件之后、路由转发之前完成请求体替换。属性配置Attributes插件完整属性如下表其中只有query是必填项名称类型必填描述querystringTrue发送给上游的 GraphQL 查询语句operation_namestringFalse操作名称仅当查询中包含多个操作operation时才必须指定variablesarrayFalse用于 GraphQL 查询的变量名列表字符串数组对照源码 apisix/plugins/degraphql.lua 中的 JSON Schema还可以看到更多隐含约束queryminLength 1、maxLength 1024即查询字符串非空且最长 1024 字符超长查询在配置校验阶段就会被拒绝variablesminItems 1即一旦配置了该字段数组内至少要有 1 个变量名数组元素类型为 stringoperation_nameminLength 1、maxLength 1024。variables是一个白名单数组里面存放的是 GraphQL 查询中变量$name、$githubAccount等的名字。客户端请求中只有被列入白名单的字段才会被提取并传入上游未被列出的请求字段会被忽略。配置校验逻辑源码级解析degraphql不仅做结构校验还会在写入配置时实际解析 GraphQL 语法。核心逻辑在_M.check_schemaapisix/plugins/degraphql.lua先按上述 Schema 做结构校验调用require(graphql).parse(conf.query)尝试解析查询语句若语法非法配置写入失败并返回failed to parse query: ...检查解析结果的definitions数量当查询中同时存在多个操作例如多个具名 query且未指定operation_name时返回错误operation_name is required if multiple operations are present in the query。测试 t/plugin/degraphql.t 的 TEST 9 完整覆盖了这四类校验失败场景对应的 Admin API 错误信息分别是缺少queryproperty query is required查询语法错误如uery {}failed to parse query: Syntax error near line 1variables为空数组expect array to have at least 1 items多操作缺operation_nameoperation_name is required if multiple operations are present in the query这意味着错误配置在写入网关时就会被拦截而不会在请求阶段才暴露从源头保证了路由配置的可靠性。准备 GraphQL 后端服务文档使用 Docker 部署一个开源的 GraphQL 演示服务npalm/graphql-java-demo镜像作为上游docker run -d --name grapql-demo -p 8080:8080 npalm/graphql-java-demo服务启动后可用的端点http://localhost:8080/graphiql—— GraphQL IDEGraphiQLhttp://localhost:8080/playground—— GraphQL IDEPrisma GraphQL Clienthttp://localhost:8080/altair—— GraphQL IDEAltair GraphQL Clienthttp://localhost:8080/—— 一个简单的 React 演示页面ws://localhost:8080/subscriptions—— WebSocket 订阅端点后续示例中APISIX 数据面监听9080端口对外提供 REST 入口Admin API 监听9180端口用于配置管理上游 GraphQL 服务监听8080。场景一无变量查询查询列表假设我们有这样一段 GraphQL 查询用于拉取人员列表的id与name字段query { persons { id name } }在http://localhost:8080/playground上执行后返回{ data: { persons: [ { id: 7, name: Niek }, { id: 8, name: Josh }, ...... ] } }现在我们希望不暴露 GraphQL 端点而是通过 APISIX 代理的 RESTful API 拿到同样数据。第一步是在 APISIX 中创建路由并启用degraphql插件把 GraphQL 查询放进插件配置。由于 JSON 中无法直接书写换行需要把查询转成 JSON 字符串{\n persons {\n id\n name\n }\n}\ncurl --location --request PUT http://localhost:9180/apisix/admin/routes/1 \ --header X-API-KEY: edd1c9f034335f136f87ad84b625c8f1 \ --header Content-Type: application/json \ --data-raw { uri: /graphql, upstream: { type: roundrobin, nodes: { 127.0.0.1:8080: 1 } }, plugins: { degraphql: { query: {\n persons {\n id\n name\n }\n}\n } } }路由创建成功后直接向 APISIX 发起 POST 请求即可触发查询curl --location --request POST http://localhost:9080/graphql返回结果与直接调用 GraphQL 完全一致{ data: { persons: [ { id: 7, name: Niek }, { id: 8, name: Josh }, ...... ] } }对应的自动化测试是 t/plugin/degraphql.t 的 TEST 1它通过 Admin API 创建同构路由后发起POST /graphql断言返回了包含 12 位人员的完整 JSON 数据。场景二带变量的查询POST 请求实际业务中查询往往带过滤条件。下面这段查询通过$name和$githubAccount两个变量过滤人员query($name: String!, $githubAccount: String!) { persons(filter: { name: $name, githubAccount: $githubAccount }) { id name blog githubAccount talks { id title } } } variables: { name: Niek, githubAccount: npalm }在 playground 中执行得到{ data: { persons: [ { id: 7, name: Niek, blog: https://040code.github.io, githubAccount: npalm, talks: [ { id: 19, title: GraphQL - The Next API Language }, { id: 20, title: Immutable Infrastructure } ] } ] } }创建路由时除了query还需要把变量名数组配置到variables字段中这样客户端才能通过 RESTful 请求传入变量值curl --location --request PUT http://localhost:9180/apisix/admin/routes/1 \ --header X-API-KEY: edd1c9f034335f136f87ad84b625c8f1 \ --header Content-Type: application/json \ --data-raw { uri: /graphql, upstream: { type: roundrobin, nodes: { 127.0.0.1:8080: 1 } }, plugins: { degraphql: { query: query($name: String!, $githubAccount: String!) {\n persons(filter: { name: $name, githubAccount: $githubAccount }) {\n id\n name\n blog\n githubAccount\n talks {\n id\n title\n }\n }\n}, variables: [ name, githubAccount ] } } }variables是数组元素为 GraphQL 查询中的变量名客户端即可用 REST 参数与之对应。接下来通过 POST 请求携带 JSON 请求体传入变量curl --location --request POST http://localhost:9080/graphql \ --header Content-Type: application/json \ --data-raw { name: Niek, githubAccount: npalm }返回结果与 GraphQL 查询一致{ data: { persons: [ { id: 7, name: Niek, blog: https://040code.github.io, githubAccount: npalm, talks: [ { id: 19, title: GraphQL - The Next API Language }, { id: 20, title: Immutable Infrastructure } ] } ] } }此场景对应测试 t/plugin/degraphql.t 的 TEST 4 / TEST 5双变量与 TEST 2 / TEST 3单变量其中 TEST 5 断言了与上文完全一致的返回体。场景三GET 请求传变量变量同样可以通过 GET 请求的查询字符串传递无需请求体。沿用场景二的路由配置curl http://localhost:9080/graphql?nameNiekgithubAccountnpalm返回结果与 POST 方式完全相同{ data: { persons: [ { id: 7, name: Niek, blog: https://040code.github.io, githubAccount: npalm, talks: [ { id: 19, title: GraphQL - The Next API Language }, { id: 20, title: Immutable Infrastructure } ] } ] } }在 GET 请求中变量通过 URL 查询字符串传递插件会将其提取并转成上游 GraphQL 所需的variables参数。对应测试为 t/plugin/degraphql.t 的 TEST 12 / TEST 13TEST 14 / TEST 15 则验证了无变量场景下 GET 请求同样可用。请求转换的源码细节理解了三个场景后我们再深入 apisix/plugins/degraphql.lua 的_M.accessL116-L157看网关内部到底做了什么方法限制。插件只接受POST与GET两种方法其他方法直接返回405 Method Not Allowed。这一点在文档中未展开但测试与源码均有明确实现。变量提取。POST 请求走fetch_post_variablesL77-L102读取请求体并强制按 JSON 解码再从解码后的表中按conf.variables白名单提取变量。请求体读取失败返回503请求体缺失返回400并记录missing request bodyTEST 6请求体不是合法 JSON 返回400并记录invalid request body cant be decodedTEST 7。GET 请求走fetch_get_variablesL105-L113通过core.request.get_uri_args()获取查询字符串参数同样按白名单提取。载荷组装与转发。组装的载荷包含三部分variablesPOST 为对象、GET 为 JSON 编码后的字符串、operationName取自conf.operation_name未配置时为空、query取自conf.queryPOST 场景强制把上游请求头Content-Type设置为application/json这正是 t/plugin/degraphql.t TEST 8 的验证点并通过ngx.req.set_body_data用组装后的 JSON 替换原始请求体GET 场景通过core.request.set_uri_args把query、operationName、variables写回查询字符串再转发给上游。一个值得注意的实现细节POST 且未配置variables时插件也会先调用core.request.get_body()读取一次请求体——源码注释说明这是set_body_data的前置要求必须先读后写保证 body 替换的语义正确。多操作查询与 operation_name当query中同时包含多个操作例如两个具名查询拼在一起时必须显式配置operation_name指明执行哪一个。其效果是组装载荷时写入operationName字段上游据此选择执行的操作。测试 t/plugin/degraphql.t 的 TEST 10 / TEST 11 构造了persons与githubAccount两个具名查询拼接的query配置operation_name: persons后请求命中persons操作并返回对应数据。若此时漏配operation_name配置写入会被check_schema拒绝见上文校验逻辑。删除插件移除degraphql插件同样简单将插件配置从路由 JSON 中删除即可APISIX 会自动热加载无需重启即可生效。先获取admin_key文档推荐用yq从 conf/config.yaml 中读取并写入环境变量admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g)然后更新路由将plugins置空curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { methods: [GET], uri: /graphql, plugins: {}, upstream: { type: roundrobin, nodes: { 127.0.0.1:8080: 1 } } }:::note 关于admin_key本文示例沿用文档中的默认 Admin Keyedd1c9f034335f136f87ad84b625c8f1。需要说明的是当前仓库自带的 conf/config.yaml 中deployment.admin.admin_key[0].key默认为空字符串APISIX 启动时会自动生成并回写该文件——因此生产环境请通过yq命令读取实际生效的 key或使用外部机制统一注入密钥避免使用固定的内置 token该配置文件的注释也明确提示了固定 API token 的安全风险。 :::总结与注意事项degraphql插件为前端 REST、后端 GraphQL的混合架构提供了轻量适配方案几个关键要点值得在落地时留意变量白名单variables数组决定客户端可传哪些参数未列入的请求字段会被忽略天然起到了参数约束作用请求方式仅支持 POST/GET其余方法返回405POST 的变量在 JSON 请求体中GET 的变量在查询字符串中内容协商转发给上游时 POST 请求的Content-Type会被强制设为application/json上游应据此解析查询长度query上限 1024 字符复杂查询需要控制长度或拆分多操作必配 operation_name配置校验阶段即强制避免歧义错误语义请求体缺失/非法 JSON 返回400读取失败返回503测试用例均有覆盖t/plugin/degraphql.t。整体而言degraphql把 GraphQL 的复杂性收敛在网关配置层业务侧只感知 REST 语义上游保持 GraphQL 能力二者通过一个可校验、可测试、可热更新的插件无缝衔接。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表