5.5路径片段与查询参数

添加或移除 URL 路径片段。

设置路径片段#

添加或移除 URL 路径片段。

HTTP 声明式请求通过 PathSegmentAttribute 特性来设置或移除路径片段。相应的 HTTP 声明式提取器实现为 PathSegmentDeclarativeExtractor 类型,该类型负责解析 PathSegmentAttribute 特性并构建 HttpRequestBuilder 实例所需的路径片段配置。

1. 添加路径片段

利用 PathSegmentAttribute 特性,可以便捷地在接口、方法或参数上添加路径片段。

cs
// 在接口定义上应用,影响所有方法[PathSegment("segment1")][PathSegment("segment2")]public interface IHttpService : IHttpDeclarative{    // 在方法上应用    [PathSegment("segment3")]    [PathSegment("segment4")]    [Get("https://furion.net/")]    Task<string> GetStringAsync();    // 在参数上应用,支持多重指定    [PathSegment("segment3")]    [Get("https://furion.net/")]    Task<string> GetStringAsync([PathSegment] string segment3, [QueryParam][QueryParam] int lastSegment);    // 在参数上可通过 Segment 属性设定默认值,同样可为 segment 参数设定,例如 string? segment = "default"    [Get("https://furion.net/")]    Task<string> GetStringAsync([PathSegment(Segment = "default")] string? segment);    // 冻结参数类型将被忽略    [Get("https://furion.net/")]    Task<string> GetStringAsync([PathSegment] CancellationToken cancellationToken);}

若存在重复的路径片段,它们将在后续追加中重复出现(如:/docs/docs/users/docs/)。

2. 移除路径片段

PathSegmentAttribute 特性中,设置 Remove = true,即表示移除该路径片段。在接口、方法或参数上应用有效。

cs
[PathSegment("segment1")] // 添加 segment1 路径片段[PathSegment("segment2", Remove = true)]           // 标记 segment2 为待移除public interface IHttpService : IHttpDeclarative{    [PathSegment("segment2")] // 添加 segment2 路径片段    [PathSegment("segment3")] // 添加 segment2 路径片段    [PathSegment("segment3", Remove = true)]           // 标记 segment3 为待移除    [Get("https://furion.net/")]    Task<string> GetStringAsync([PathSegment(Remove = true)] string seg);   // 动态根据 seg 值标记为待移除}

在发送 HTTP 请求之前,将移除配置中指定的待移除路径片段集合。也就是说,移除操作会在所有设置操作调用之后执行。

在上述示例中,尽管 GetStringAsync 方法尝试通过 [PathSegment] 特性添加 segment2segment3 路径片段,但由于随后分别有 [PathSegment("segment2", Remove = true)][PathSegment("segment3", Remove = true)] 特性仅指定了 Remove = true 属性,因此这两个键在最终构建请求 URL 时会被移除。只有 segment1 路径片段会保留在请求 URL 中。

PathSegmentAttribute 包含以下构造函数和属性:

  • 构造函数

    • new():作用于参数时有效,表示添加路径片段,路径片段为参数值。
    • new(segment):作用于方法或接口时,则表示添加指定路径片段操作;作用于参数且参数值为 null 时,表示添加路径片段,路径片段为参数 segment 的值。
  • 属性

    • Segment:路径片段(string 类型),特性作用于参数且参数值为 null 时,可用作默认值。
    • Remove:是否标记为待删除(bool 类型),默认值为 false(追加)。

设置查询参数(URL 参数)#

添加、修改或移除 URL 查询参数。

HTTP 声明式请求通过 QueryParamAttribute 特性来设置或移除查询参数。相应的 HTTP 声明式提取器实现为 QueryParamDeclarativeExtractor 类型,该类型负责解析 QueryParamAttribute 特性并构建 HttpRequestBuilder 实例所需的查询参数配置。

1. 添加查询参数

利用 QueryParamAttribute 特性,可以便捷地在接口、方法或参数上添加查询参数。

cs
// 在接口定义上应用,影响所有方法[QueryParam("query1", "value1")][QueryParam("query2", "value2")]public interface IHttpService : IHttpDeclarative{    // 在方法上应用    [QueryParam("query3", "value3")]    [QueryParam("query4", "value4")]    [Get("https://furion.net/")]    Task<string> GetStringAsync();    // 在参数上应用,支持 AliasAs 属性指定别名,且可多重指定    [QueryParam("query3", "value3")]    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam] string query4, [QueryParam][QueryParam(AliasAs = "query5")] int lastQuery);    // 在参数上可通过 Value 属性设定默认值,同样可为 age 参数设定,例如 int? age = 30    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam(Value = 30)] int? age);    // 支持将对象作为查询参数,并指定前缀    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam(Prefix = "user")] object obj);    // 支持 [AliasAs] 定义别名    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam][AliasAs("query5")] int lastQuery);    // 支持忽略空值参数,若 str1 值为 null 则忽略    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam(IgnoreNullValues = true)] string? str1, [QueryParam] string? str2);    // 支持 format 格式化    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam(Format = "yyyyMMdd")] DateTime date);    // 冻结参数类型将被忽略    [Get("https://furion.net/")]    Task<string> GetStringAsync([QueryParam] CancellationToken cancellationToken);}

若存在重复的查询参数键,它们将合并成多个键值对(如 key1=value1&key1=value2)。通过设置 Replace = true 属性,可以覆盖先前的查询参数和原始 URL 地址参数。默认情况下,值为 null 的查询参数会被添加到 URL 中;若需忽略这些参数,可设置 IgnoreNullValues = true

2. 移除查询参数

QueryParamAttribute 特性中,仅指定查询参数键而不赋值,即表示移除该参数。在接口或方法上应用有效。

cs
[QueryParam("query1", "value1")] // 添加 query1 参数[QueryParam("query2")]           // 标记 query2 为待移除public interface IHttpService : IHttpDeclarative{    [QueryParam("query2", "value2")] // 添加 query2 参数    [QueryParam("query3", "value3")] // 添加 query3 参数    [QueryParam("query3")]           // 标记 query3 为待移除    [Get("https://furion.net/")]    Task<string> GetStringAsync();}

在发送 HTTP 请求之前,将移除配置中指定的待移除查询参数集合。也就是说,移除操作会在所有设置操作调用之后执行。

在上述示例中,尽管 GetStringAsync 方法尝试通过 [QueryParam] 特性添加 query2query3 参数,但由于随后分别有 [QueryParam("query2")][QueryParam("query3")] 特性仅指定了查询参数键而未赋值,因此这两个键在最终构建请求 URL 时会被移除。只有 query1 参数会保留在请求 URL 中。

3. URL 参数格式化程序

在设置 HTTP 请求的查询参数时,框架会将参数键和值传递给 IUrlParameterFormatter 进行格式化。默认实现 UrlParameterFormatter 会为每个值生成一个 key=value 形式的键值对。但某些类型(如 DateTime)可能需要特殊处理,或希望改变整个键值对的输出形态(例如将多个值输出为 key[0]=val1&key[1]=val2 这样的数组格式),此时可以通过自定义格式化程序实现。

以下示例展示如何重写 Format 方法,以便将 DateTime 类型的值格式化为 yyyyMMdd 格式,其余类型使用默认处理:

csharp
public class CustomUrlParameterFormatter : UrlParameterFormatter{    /// <inheritdoc />    public override IEnumerable<KeyValuePair<string, string?>>? Format(UrlFormattingContext context, string key, IEnumerable<object?> values)    {        foreach (var value in values)        {            if (value is DateTime dateTime)            {                yield return new(key, dateTime.ToString("yyyyMMdd"));   // 格式化                continue;            }            yield return new(key, FormatValue(context, value));        }    }}

完成自定义格式化程序后,可以在配置 HttpRemoteOptions 时将其注册为默认的 URL 参数格式化器:

csharp
services.AddHttpRemote(builder => {})    .ConfigureOptions(options =>    {        options.UrlParameterFormatter = new CustomUrlParameterFormatter();    });

如此一来,在构建 URL 查询参数时,若遇到 DateTime 类型的值,框架将自动将其格式化为 yyyyMMdd 格式的字符串,从而确保输出符合预期。

4. URL 参数排序

尽管对 URL 查询参数排序的需求相对少见,但在一些对安全性要求较高的系统中,往往需要验证参数的顺序。框架为此提供了排序支持,排序对象为最终的键值对集合:

cs
HttpRequestBuilder.Get("https://furion.net/")    .WithQueryParameters(new { name = "furion", id = 1})    .SetQueryParametersSorter(pairs => pairs.OrderBy(kv => kv.Key));

通过 .SetQueryParametersSorter() 方法配置查询参数排序规则。该方法接收一个 KeyValuePair<string, string?> 序列,返回排序后的新序列。为 null 时不排序(原始添加顺序)。

QueryParamAttribute 包含以下构造函数和属性:

  • 构造函数

    • new():作用于参数时有效,表示添加查询参数,默认键为参数名。
    • new(name):作用于方法或接口时,则表示移除指定查询参数操作;作用于参数时,表示添加查询参数,键为参数 name 的值。
    • new(name, value):作用于接口、方法或参数,表示添加查询参数,键为参数 name 的值,优先级低于 AliasAs 属性。
  • 属性

    • Name:查询参数键(string 类型),优先级低于 AliasAs 属性。
    • Value:查询参数的值(object 类型),当特性作用于参数时,表示默认值。
    • AliasAs:查询参数键别名(string 类型),优先级高于 Name 属性。
    • Prefix:查询参数前缀(string 类型),仅对象参数有效。
    • Replace:是否替换已存在的查询参数(bool 类型),默认值为 false(追加)。
    • IgnoreNullValues:是否忽略空值(null)的查询参数(bool 类型),默认值为 false(不忽略)。
    • Format:要使用的格式(string? 类型),仅当 Value 实现 IFormattable 时有效。