请求转换

请求转换包括请求路径、查询、HTTP 版本、方法和标头。 在代码中,这些对象由 RequestTransformContext 对象表示,并由抽象类 RequestTransform 的实现处理。

注释:

  • 代理请求方案 (http/https)、颁发机构和路径库取自目标服务器地址(上面的示例中为 https://localhost:10001/Path/Base),不应通过转换进行修改。
  • 主机标头可以通过不依赖于授权的转换来替代,请参阅下面的 RequestHeader
  • 构造代理请求时,不会使用请求的原始 PathBase 属性,请参阅 X 转发
  • 默认情况下,所有传入的请求标头都会复制到代理请求,但主机标头除外(请参阅 Defaults默认值)。 默认情况下还会添加 X-Forwarded 标头。 这些行为可以使用以下转换进行配置。 可以指定其他请求标头,也可以通过将请求标头设置为空值来排除请求标头。

以下是通过其主要配置键识别的内置转换。 这些转换按路由配置中指定的顺序应用。

PathPrefix

修改添加前缀值的请求路径

密钥 价值 必选
PathPrefix 以“/”开头的路径 是的

Config:

{ "PathPrefix": "/prefix" }

代码:

routeConfig = routeConfig.WithTransformPathPrefix(prefix: "/prefix");
transformBuilderContext.AddPathPrefix(prefix: "/prefix");

示例:
/request/path 变为 /prefix/request/path

这将为请求路径加上给定值前缀。

PathRemovePrefix

修改删除前缀值的请求路径

密钥 价值 必选
PathRemovePrefix 以“/”开头的路径 是的

Config:

{ "PathRemovePrefix": "/prefix" }

代码:

routeConfig = routeConfig.WithTransformPathRemovePrefix(prefix: "/prefix");
transformBuilderContext.AddPathRemovePrefix(prefix: "/prefix");

示例:
/prefix/request/path 变为 /request/path
/prefix2/request/path 未修改

这将从请求路径中删除匹配的前缀。 匹配是在路径段边界(/) 上进行的。 如果前缀不匹配,则不会进行更改。

PathSet

将请求路径替换为指定的值

密钥 价值 必选
PathSet 以“/”开头的路径 是的

Config:

{ "PathSet": "/newpath" }

代码:

routeConfig = routeConfig.WithTransformPathSet(path: "/newpath");
transformBuilderContext.AddPathSet(path: "/newpath");

示例:
/request/path 变为 /newpath

这将使用给定值设置请求路径。

路径模式

使用模式模板替换请求路径

密钥 价值 必选
路径模式 以“/”开头的路径模板 是的

Config:

{ "PathPattern": "/my/{plugin}/api/{**remainder}" }

代码:

routeConfig = routeConfig.WithTransformPathRouteValues(pattern: new PathString("/my/{plugin}/api/{**remainder}"));
transformBuilderContext.AddPathRouteValues(pattern: new PathString("/my/{plugin}/api/{**remainder}"));

这将使用给定值设置请求路径,并将任何 {} 段替换为关联的路由值。 {} 删除了不带匹配路由值的段。 最后的{}段可以标记为{**remainder},以指示这是一个可能包含多个路径段的总括段。 有关路由模板的详细信息,请参阅 ASP.NET Core 的 路由文档

示例:

步骤 价值
路由定义 /api/{plugin}/stuff/{**remainder}
请求路径 /api/v1/stuff/more/stuff
插件值 v1
余数值 more/stuff
路径模式 /my/{plugin}/api/{**remainder}
结果 /my/v1/api/more/stuff

QueryValueParameter

在请求查询字符串中添加或替换参数

密钥 价值 必选
QueryValueParameter 查询字符串参数的名称 是的
设置/追加 静态值 是的

Config:

{
  "QueryValueParameter": "foo",
  "Append": "bar"
}

代码:

routeConfig = routeConfig.WithTransformQueryValue(queryKey: "foo", value: "bar", append: true);
transformBuilderContext.AddQueryValue(queryKey: "foo", value: "bar", append: true);

这将添加一个包含名称 foo 的查询字符串参数,并将其设置为静态值 bar

示例:

步骤 价值
查询 ?a=b
QueryValueParameter foo
追加 remainder
结果 ?a=b&foo=remainder

查询路由参数

使用路由配置中的值添加或替换查询字符串参数

密钥 价值 必选
查询路由参数 查询字符串参数的名称 是的
设置/追加 路由值的名称 是的

Config:

{
  "QueryRouteParameter": "foo",
  "Append": "remainder"
}

代码:

routeConfig = routeConfig.WithTransformQueryRouteValue(queryKey: "foo", routeValueKey: "remainder", append: true);
transformBuilderContext.AddQueryRouteValue(queryKey: "foo", routeValueKey: "remainder", append: true);

这将添加一个包含名称 foo 的查询字符串参数,并将其设置为关联的路由值的值。

示例:

步骤 价值
路由定义 /api/{*remainder}
请求路径 /api/more/stuff
余数值 more/stuff
查询路由参数 foo
追加 remainder
结果 ?foo=more/stuff

QueryRemoveParameter

从请求查询字符串中删除指定的参数

密钥 价值 必选
QueryRemoveParameter 查询字符串参数的名称 是的

Config:

{ "QueryRemoveParameter": "foo" }

代码:

routeConfig = routeConfig.WithTransformQueryRemoveKey(queryKey: "foo");
transformBuilderContext.AddQueryRemoveKey(queryKey: "foo");

如果请求中存在,这将删除具有名称 foo 的查询字符串参数。

示例:

步骤 价值
请求路径 ?a=b&foo=c
QueryRemoveParameter foo
结果 ?a=b

HttpMethodChange

更改请求中使用的 http 方法

密钥 价值 必选
HttpMethodChange 需要替换的 HTTP 方法 是的
设置 新的 http 方法 是的

Config:

{
  "HttpMethodChange": "PUT",
  "Set": "POST"
}

代码:

routeConfig = routeConfig.WithTransformHttpMethodChange(fromHttpMethod: HttpMethods.Put, toHttpMethod: HttpMethods.Post);
transformBuilderContext.AddHttpMethodChange(fromHttpMethod: HttpMethods.Put, toHttpMethod: HttpMethods.Post);

这将将 PUT 请求更改为 POST。

RequestHeadersCopy

设置是否将传入请求标头复制到出站请求

密钥 价值 违约 必选
RequestHeadersCopy 真/假 是的

Config:

{ "RequestHeadersCopy": "false" }

代码:

routeConfig = routeConfig.WithTransformCopyRequestHeaders(copy: false);
transformBuilderContext.CopyRequestHeaders = false;

这将设置是否将所有传入请求标头复制到代理请求中。 此设置默认处于启用状态,可通过将转换配置为 false 值来禁用。 即使禁用了此功能,引用特定标头的转换仍将运行。

RequestHeaderOriginalHost

指定是否应将传入请求主机标头复制到代理请求

密钥 价值 违约 必选
RequestHeaderOriginalHost 真/假 是的

Config:

{ "RequestHeaderOriginalHost": "true" }
routeConfig = routeConfig.WithTransformUseOriginalHostHeader(useOriginal: true);
transformBuilderContext.AddOriginalHost(true);

这指定是否应将传入请求主机标头复制到代理请求。 此设置默认处于禁用状态,可以通过配置具有 true 值的转换来启用此设置。 直接引用 Host 标头的转换将替代此转换。

RequestHeader

添加或替换请求标头

密钥 价值 必选
RequestHeader 标头名称 是的
设置/追加 标头值 是的

Config:

{
  "RequestHeader": "MyHeader",
  "Set": "MyValue"
}

代码:

routeConfig = routeConfig.WithTransformRequestHeader(headerName: "MyHeader", value: "MyValue", append: false);
transformBuilderContext.AddRequestHeader(headerName: "MyHeader", value: "MyValue", append: false);

示例:

MyHeader: MyValue

这会设置或追加命名标头的值。 Set 替换任何现有标头。 Append 会添加具有给定值的额外标头。 注意:不建议将“”设置为标头值,并可能导致未定义的行为。

RequestHeaderRouteValue

使用路由配置中的值添加或替换标头

密钥 价值 必选
RequestHeader 查询字符串参数的名称 是的
设置/追加 路由值的名称 是的

Config:

{
  "RequestHeaderRouteValue": "MyHeader",
  "Set": "MyRouteKey"
}

代码:

routeConfig = routeConfig.WithTransformRequestHeaderRouteValue(headerName: "MyHeader", routeValueKey: "key", append: false);
transformBuilderContext.AddRequestHeaderRouteValue(headerName: "MyHeader", routeValueKey: "key", append: false);

示例:

步骤 价值
路由定义 /api/{*remainder}
请求路径 /api/more/stuff
余数值 more/stuff
RequestHeaderFromRoute foo
追加 remainder
结果 foo: more/stuff

这会使用路由配置中的值来设置或追加命名标头的值。 Set 替换任何现有标头。 Append 会添加具有给定值的额外标头。 注意:不建议将“”设置为标头值,并可能导致未定义的行为。

RequestHeaderRemove

删除请求标头

密钥 价值 必选
RequestHeaderRemove 标头名称 是的

Config:

{
  "RequestHeaderRemove": "MyHeader"
}

代码:

routeConfig = routeConfig.WithTransformRequestHeaderRemove(headerName: "MyHeader");
transformBuilderContext.AddRequestHeaderRemove(headerName: "MyHeader");

示例:

MyHeader: MyValue
AnotherHeader: AnotherValue

这会删除命名标头。

RequestHeadersAllowed

密钥 价值 必选
RequestHeadersAllowed 以分号分隔的允许标头名称列表。 是的

Config:

{
  "RequestHeadersAllowed": "Header1;header2"
}

代码:

routeConfig = routeConfig.WithTransformRequestHeadersAllowed("Header1", "header2");
transformBuilderContext.AddRequestHeadersAllowed("Header1", "header2");

YARP 默认将大多数请求标头复制到代理请求(请参阅 RequestHeadersCopy)。 某些安全模型仅允许代理特定标头。 此转换将禁用 RequestHeadersCopy,并且仅复制给定标头。 如果未包含在允许列表中,则修改或追加到现有标头的其他转换可能会受到影响。

请注意,默认情况下,某些标头 YARP 不会复制,因为它们特定于连接或其他安全敏感(例如Connection)。 Alt-Svc 将这些标头名称放在允许列表中将绕过该限制,但强烈建议不要这样做,因为它可能会对代理的功能产生负面影响或导致安全漏洞。

示例:

Header1: value1
Header2: value2
AnotherHeader: AnotherValue

只有 header1 和 header2 被复制到代理请求中。

X 转发

添加包含原始客户端请求信息的标头

密钥 价值 违约 必选
X 转发 要应用于下面列出的所有 X-Forwarded* 的默认操作(设置、追加、删除、关闭) 设置 是的
要应用于此标头的操作 * 请参阅 X-Forwarded
协议 要应用于此标头的操作 * 请参阅 X-Forwarded
主机 要应用于此标头的操作 * 请参阅 X-Forwarded
前缀 要应用于此标头的操作 * 请参阅 X-Forwarded
HeaderPrefix 标头名称前缀 “X-Forwarded-”

操作“关闭状态”会完全禁用转换功能。

Config:

{
  "X-Forwarded": "Set",
  "For": "Remove",
  "Proto": "Append",
  "Prefix": "Off",
  "HeaderPrefix": "X-Forwarded-"
}

代码:

routeConfig = routeConfig.WithTransformXForwarded(
  headerPrefix = "X-Forwarded-",
  ForwardedTransformActions xDefault = ForwardedTransformActions.Set,
  ForwardedTransformActions? xFor = null,
  ForwardedTransformActions? xHost = null,
  ForwardedTransformActions? xProto = null,
  ForwardedTransformActions? xPrefix = null);
transformBuilderContext.AddXForwarded(ForwardedTransformActions.Set);
transformBuilderContext.AddXForwardedFor(headerName: "X-Forwarded-For", ForwardedTransformActions.Append);
transformBuilderContext.AddXForwardedHost(headerName: "X-Forwarded-Host", ForwardedTransformActions.Append);
transformBuilderContext.AddXForwardedProto(headerName: "X-Forwarded-Proto", ForwardedTransformActions.Off);
transformBuilderContext.AddXForwardedPrefix(headerName: "X-Forwarded-Prefix", ForwardedTransformActions.Remove);

示例:

X-Forwarded-For: 5.5.5.5
X-Forwarded-Proto: https
X-Forwarded-Host: IncomingHost:5000
X-Forwarded-Prefix: /path/base

禁用默认标头:

{ "X-Forwarded": "Off" }
transformBuilderContext.UseDefaultForwarders = false;

当代理连接到目标服务器时,连接与客户端对代理建立的连接无关。 目标服务器可能需要原始连接信息进行安全检查,并正确生成链接和重定向的绝对 URI。 若要将有关客户端连接的信息传递给目标,可以添加一组额外的标头。 在Forwarded标准建立之前,一种常见的解决方案是使用X-Forwarded-*标头。 没有定义X-Forwarded-*标头的官方标准,因此实现可能因服务器而异,请检查目标服务器是否支持。

即使路由配置中未指定,也默认启用此转换。

X-Forwarded 值设置为包含你需要启用的标头的逗号分隔列表。 默认情况下,所有 for 标头都处于启用状态。 可以通过指定值 "Off"来禁用所有值。

Prefix 指定要用于每个标头的标头名称前缀。 使用默认X-Forwarded-前缀时,生成的标头将为X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-HostX-Forwarded-Prefix

转换操作指定了如何将每个标头与同名的现有标头组合在一起。 它可以是“Set”、“Append”、“Remove”或“Off”(完全禁用转换功能)。 遍历多个代理的请求可能会累积此类标头的列表,目标服务器需要评估列表以确定原始值。 如果操作是“Set”并且关联的值在请求中不可用(例如 RemoteIpAddress 为 null),则仍会删除现有的任何标头以防止欺骗。

{Prefix}For 标头值取自 HttpContext.Connection.RemoteIpAddress 表示前一个调用方 IP 地址。 不包括端口。 IPv6 地址不包括方括号 []

{Prefix}Proto 标头值取自 HttpContext.Request.Scheme 指示前一个调用方是否使用了 HTTP 或 HTTPS。

{Prefix}主机标头值取自传入请求的主机标头。 这独立于上面指定的 RequestHeaderOriginalHost。 Unicode/IDN 主机经过 punycode 编码。

{Prefix}Prefix 标头值取自 HttpContext.Request.PathBase。 生成代理请求时不使用 PathBase 属性,因此目标服务器需要原始值才能正确生成链接和重定向。 该值采用百分比编码 URI 格式。

转发

添加包含原始客户端请求信息的标头

密钥 价值 违约 必选
转发 逗号分隔列表,其中包含以下任何值:for、by、proto、host (无) 是的
ForFormat Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/unknownAndRandomPort/ip/IpAndPort/IpAndRandomPort 随机
ByFormat Random/RandomAndPort/RandomAndRandomPort/Unknown/UnknownAndPort/unknownAndRandomPort/ip/IpAndPort/IpAndRandomPort 随机
行动 应用于此标头的操作(设置、附加、删除、关闭) 设置

Config:

{
  "Forwarded": "by,for,host,proto",
  "ByFormat": "Random",
  "ForFormat": "IpAndPort",
  "Action": "Append"
},

代码:

routeConfig = routeConfig.WithTransformForwarded(useHost: true, useProto: true, forFormat: NodeFormat.IpAndPort, ByFormat: NodeFormat.Random, action: ForwardedTransformAction.Append);
transformBuilderContext.AddForwarded(useHost: true, useProto: true, forFormat: NodeFormat.IpAndPort, ByFormat: NodeFormat.Random, action: ForwardedTransformAction.Append);

示例:

Forwarded: proto=https;host="localhost:5001";for="[::1]:20173";by=_YQuN68tm6

标头 ForwardedRFC 7239 定义。 它整合了与非官方 X-Forwarded 标头相同的许多功能,将信息流向目标服务器,否则将使用代理隐藏这些信息。

启用此转换将禁用默认的 X-Forwarded 转换,因为它们以其他格式传递类似的信息。 仍可以显式启用 X-Forwarded 转换。

操作:这指定转换应如何处理现有的 Forwarded 标头。 它可以是“Set”、“Append”、“Remove”或“Off”(完全禁用转换功能)。 遍历多个代理的请求可能会累积此类标头的列表,目标服务器需要评估列表以确定原始值。

Proto:此值取自 HttpContext.Request.Scheme 指示前一个调用方是否使用了 HTTP 或 HTTPS。

主机:此值取自传入请求的主机标头。 这独立于上面指定的 RequestHeaderOriginalHost。 Unicode/IDN 主机经过 punycode 编码。

对于:此值标识以前的调用方。 IP 地址取自 HttpContext.Connection.RemoteIpAddress。 有关详细信息,请参阅下面的 ByFormat 和 ForFormat。

接收源:此值标识代理接收请求的位置。 IP 地址取自 HttpContext.Connection.LocalIpAddress。 有关详细信息,请参阅下面的 ByFormat 和 ForFormat。

ByFormat 和 ForFormat:

RFC 允许 By 和 For 字段 的各种格式 。 它要求默认格式使用此处标识为 Random 的经过模糊处理的标识符。

格式 DESCRIPTION 示例:
随机 每个请求随机生成的经过模糊处理的标识符。 这允许诊断跟踪方案,同时出于隐私原因限制唯一标识信息的流。 by=_YQuN68tm6
RandomAndPort 随机标识符加上端口。 by="_YQuN68tm6:80"
RandomAndRandomPort 随机标识符加上端口的另一个随机标识符。 by="_YQuN68tm6:_jDw5Cf3tQ"
未知 如果上述实体的标识未知,但代理服务器仍希望表明请求已转发,则可以使用此方法。 by=unknown
UnknownAndPort 未知标识符和端口(如果可用)。 by="unknown:80"
UnknownAndRandomPort 端口的未知标识符和随机标识符。 by="unknown:_jDw5Cf3tQ"
Ip IPv4 地址或 IPv6 地址,包括括号。 by="[::1]"
IpAndPort IP 地址和端口。 by="[::1]:80"
IpAndRandomPort 端口的 IP 地址加上随机标识符。 by="[::1]:_jDw5Cf3tQ"

ClientCert

将入站连接上使用的客户端证书作为标头转发到目标

密钥 价值 必选
ClientCert 标头名称 是的

Config:

{ "ClientCert": "X-Client-Cert" }

代码:

routeConfig = routeConfig.WithTransformClientCertHeader(headerName: "X-Client-Cert");
transformBuilderContext.AddClientCertHeader(headerName: "X-Client-Cert");

示例:

X-Client-Cert: SSdtIGEgY2VydGlmaWNhdGU...

由于入站和出站连接是独立的,因此需要有一种方法将任何入站客户端证书传递到目标服务器。 此转换会导致从 HttpContext.Connection.ClientCertificate 中获取的客户端证书进行 Base64 编码,并设置为给定标头名称的值。 目标服务器可能需要该证书对客户端进行身份验证。 没有定义该标头的标准,不同的实现方式可能为此有所不同,请检查目标服务器是否支持。

默认情况下,服务器对传入客户端证书进行最小验证。 应在代理或目标中验证证书,有关详细信息,请参阅 客户端证书身份验证 文档。

仅当连接上已存在客户端证书时,此转换才适用。 如果需要根据每个路由从客户端请求证书,请参阅 可选证书文档