如何设计C#类构造符合限制规则的API请求筛选参数
实现思路
采用强类型+编译期约束的方案,完全避免硬编码字符串,同时只暴露你需要开放的筛选器,从语法层面限制非法输入。
第一步:定义基础结构
首先定义最终要序列化的Filter结构,以及筛选器的抽象基类,封装通用逻辑:
// 最终要序列化发送给API的Filter结构 public class Filter { public string FilterName { get; set; } public string Operator { get; set; } public List<string> Values { get; set; } = new(); } // 筛选器抽象基类,封装通用属性和构建逻辑 public abstract class FilterDefinition { internal abstract string FilterName { get; } internal string? Operator { get; set; } internal List<string> Values { get; } = new(); public Filter Build() => new() { FilterName = FilterName, Operator = Operator ?? throw new InvalidOperationException("未设置筛选运算符"), Values = Values }; }
第二步:实现开放的筛选器
对你需要暴露的少数筛选器单独实现,每个筛选器只开放自身支持的运算符,从接口层面限制非法输入:
// 城市筛选器,支持is/contains public class CityFilter : FilterDefinition { internal override string FilterName => "City"; // 仅允许传入该枚举值,编译期自动拦截非法运算符 public enum AllowedOperators { Is, Contains } public CityFilter AddOperator(AllowedOperators op) { Operator = op.ToString().ToLower(); return this; } public CityFilter AddValue(string cityName) { Values.Add(cityName); return this; } } // 州筛选器,仅支持is public class StateFilter : FilterDefinition { internal override string FilterName => "state"; public enum AllowedOperators { Is } public StateFilter AddOperator(AllowedOperators op) { Operator = op.ToString().ToLower(); return this; } public StateFilter AddValue(string state) { Values.Add(state); return this; } } // 街道号筛选器,支持range/is/contains public class StreetNumberFilter : FilterDefinition { internal override string FilterName => "streetNumber"; public enum AllowedOperators { Range, Is, Contains } public StreetNumberFilter AddOperator(AllowedOperators op) { Operator = op.ToString().ToLower(); return this; } public StreetNumberFilter AddValue(string number) { Values.Add(number); return this; } }
如果你更偏好字符串入参,可以把AddOperator的参数改为字符串,内部增加合法值校验,运行时抛出异常提示即可,不用枚举的话写法更贴近你给出的预期调用示例。
第三步:实现构造器入口
封装构造逻辑,仅允许传入你定义过的合法筛选器:
// 筛选数组构造器 public class FilterArrayBuilder { private readonly List<Filter> _filters = new(); public FilterArrayBuilder AddFilter(FilterDefinition filterDef) { _filters.Add(filterDef.Build()); return this; } public List<Filter> Build() => _filters.ToList(); } // 静态入口类,简化调用 public static class FilterBuilder { public static FilterArrayBuilder FilterArray() => new(); public static CityFilter Cities() => new(); public static StateFilter State() => new(); public static StreetNumberFilter StreetNumber() => new(); }
调用示例
完全强类型,编码时会有智能提示,非法输入直接编译报错:
var filters = FilterBuilder.FilterArray() .AddFilter(FilterBuilder.Cities() .AddOperator(CityFilter.AllowedOperators.Contains) .AddValue("杭州")) .AddFilter(FilterBuilder.State() .AddOperator(StateFilter.AllowedOperators.Is) .AddValue("浙江省")) .Build(); // 序列化filters即可得到符合要求的JSON请求参数
方案优势
- 编译期校验:非法的筛选器、运算符直接在编码阶段报错,无需等到运行时调用接口才发现问题
- 按需开放:API提供的100种筛选器不需要全部实现,只需要实现业务用到的少数几种即可
- 可维护性强:新增筛选器只需新增对应类,修改规则只需要调整对应筛选器的允许枚举即可,无需改动全局逻辑
- 无魔法字符串:所有筛选名、运算符都由代码封装,不会出现拼写错误
内容的提问来源于stack exchange,提问作者Mb...R
相关产品推荐
相关产品推荐

