标题传播
配置将 HTTP 标题传播到子图
您可以为GraphOS 路由器或 Apollo 路由核心包含在其向每个子图发出的请求中。子图。您可以定义针对每个子图的标题规则,以及适用于所有子图的规则。
您在您的YAML 配置文件中定义标题规则,如下所示:
# ...other configuration...headers:all: # Header rules for all subgraphsrequest:- propagate:matching: ^upstream-header-.*- remove:named: "x-legacy-account-id"subgraphs:products: # Header rules for just the products subgraphrequest:- insert:name: "router-subgraph-name"value: "products"
支持的标题规则
该路由器支持以下类型的标题规则:
传播
允许您有选择地传递客户端请求中包含的标题到路由器。
您可以根据匹配的正则表达式模式来指定要传播哪些标题:
- propagate:matching: .*
ⓘ 注意
在通过模式传播时,路由器从未传播所谓的端到端头(如Content-Length
)。
或者,您可以通过named
选项提供一个静态字符串。named
配置具有额外的灵活性,因为它们支持以下选项:
default
:若客户端未发送值,则设置该值rename
:将头部键重命名为提供的值
- propagate:named: "x-user-id"default: "abc123"rename: "account-id"
remove
选择性删除客户端发往propagate
类似,此选项可以匹配静态字符串或正则表达式。
# Do not send this subgraph the "Cookie" header.- remove:named: "Cookie"- remove:# Remove headers that include the legacy 'x-' prefix.matching: ^x-.*$
insert
允许您向发往特定子图的请求添加自定义头部。这些头部是字符串(静态的、来自请求数据包或上下文),它们来自
- 插入静态头部
- insert:name: "sent-from-our-apollo-router"value: "indeed"
- 从上下文插入头部
- insert:name: "sent-from-our-apollo-router-context"from_context: "my_key_in_context"
- 从请求数据包中插入头部
- insert:name: "sent-from-our-apollo-router-request-body"path: ".operationName" # It's a JSON path query to fetch the operation name from request bodydefault: "UNKNOWN" # If no operationName has been specified
示例JSON路径查询
假设您有一个以下结构的JSON请求数据包
{"query": "{ products { id name } }","extensions": {"metadata": [{"app_name": "random_app_name"}]}}
要获取字段 app_name
的值,对应的路径是.extensions.metadata[0].app_name
。
JSON路径查询始终以点.
开始
使用此配置
headers:all:request:- insert:name: from_app_namepath: .extensions.metadata[0].app_name
您将为所有子图传递一个头部:"from_app_name": "random_app_name"
规则顺序
头部规则按其声明的顺序应用,后来的规则可以覆盖较早规则的效果。考虑以下示例:
❌
headers:all:request:- remove:named: "test"- propagate:matching: .*
在此示例中,首先从传播列表中移除任何名为test
的头部。然而,传播头部的列表当前为空!接下来,propagate
规则会把所有头部添加到传播列表中,包括test
。
要正确地从传播列表中删除头部,请确保将您的remove
规则定义在任何propagate
规则之后:
✅
headers:all:request:- propagate:matching: .*- remove:named: "test"
使用此排序,首先将所有头部添加到传播列表,接着从列表中移除test
头部。
示例
以下是一个包含所有可能配置选项的完整示例
headers:# Header rules for all subgraphsall:request:# Propagate matching headers- propagate:matching: ^upstream-header-.*# Propagate matching headers- propagate:named: "some-header"default: "default-value"rename: "destination-header"# Remove the "x-legacy-account-id" header- remove:named: "x-legacy-account-id"# Remove matching headers- remove:matching: ^x-deprecated-.*# Insert the 'my-company' header- insert:name: "my-company"value: "acme"# Subgraph-specific header rulessubgraphs:products:request:# Calls to the products subgraph have the "router-subgraph-name" header set to `products`.- insert:name: "router-subgraph-name"value: "products"accounts:request:# Calls to the accounts subgraph have the "router-subgraph-name" header set to `accounts`.- insert:name: "router-subgraph-name"value: "accounts"
响应头传播
目前无法仅使用YAML配置从子图传播响应头到客户端。然而,您可以使用Rhai脚本实现这一点。
这种方法依赖于每个请求都有一个context
对象,可以在此请求期间存储数据:
- 对于每个子图响应,将头部值复制到上下文中。
- 对于超级图响应,将上下文中的头部值复制到响应上。
以下是使用了Rhai脚本的router.yaml
示例:
rhai:main: "main.rhai"
以下是收集来自子图的set-cookie
头部并将它们合并到单个客户端响应头部的Rhai脚本示例:
fn supergraph_service(service) {let add_cookies_to_response = |response| {if response.context["set_cookie_headers"]?.len > 0 {response.headers["set-cookie"] = response.context["set_cookie_headers"];}};service.map_response(add_cookies_to_response);}fn subgraph_service(service, subgraph) {let store_cookies_from_subgraphs = |response| {if "set-cookie" in response.headers {if response.context["set_cookie_headers"] == () {response.context.set_cookie_headers = []}response.context.set_cookie_headers += response.headers.values("set-cookie");}};service.map_response(store_cookies_from_subgraphs);}
ⓘ 注意
如果您需要一个基于配置的解决方案来传播响应头,请在我们的问题跟踪器上留言。
子图间的传播
目前无法仅使用YAML配置在子图之间传播头部。然而,您可以使用Rhai脚本实现这一点。
这种方法依赖于每个请求都有一个context
对象,可以在此请求期间存储数据:
- 对于每个子图响应,将头部值复制到上下文中。
- 对于每个子图请求,将上下文中的头部值复制入子图请求。
以下是使用了Rhai脚本的router.yaml
示例:
rhai:main: "main.rhai"
以下是复制request-id
和user
头部的Rhai脚本示例:
fn subgraph_service(service, subgraph) {// The list of headers that you which to propagate.let headers = ["request-id", "user"];// Callback for subgraph requests. Inserts headers from context into the subgraph request.let request_callback = |request| {for key in headers {if request.context[key] != () {request.subgraph.headers[key] = request.context[key];}}};// Callback for subgraph responses. Pulls header values out of the response and inserts them into context.let response_callback = |response| {for key in headers {if key in response.headers {response.context[key] = response.headers[key];}}};// Register the callbacks.service.map_request(request_callback);service.map_response(response_callback);}