EMD安全
查询语法是基于entity data model(EDM),不是基于底层的model类型,可以从EDM排除一个属性,这样这个属性在client就不能被查询了。
有两种方式可以从EDM中排除一个属性
第一种使用 [IgnoreDataMember]特性
public class Employee { public string Name { get; set; } public string Title { get; set; }
//当应用于类型的成员时,指定该成员不是数据协定的一部分且没有进行序列化 命名空间System.Runtime.Serialization [IgnoreDataMember] public decimal Salary { get; set; } // Not visible in the EDM }
第二种方式以编程方式从EDM中排除属性
var employees = modelBuilder.EntitySet<Employee>("Employees"); employees.EntityType.Ignore(emp => emp.Salary);
安全查询
[Queryable]特性是一个action过滤器支持解析、验证以及应用查询,过滤器将查询选项转为一个LINQ表达式。
Web API支持以下OData查询选项:
为了使用OData的查询,必须显示指定查询可用。可以在全局进行配置或在指定的控制器或action上。
public static void Register(HttpConfiguration config) { config.EnableQuerySupport(); // ... }
public class ProductsController : ApiController { [Queryable] IQueryable<Product> Get() {} }
限制查询
为了安全或性能方面的考虑,可能需要限制一些操作。Queryable]特性有一些内建的属性可以实现这一点。如下所示:
//仅允许$skip与$top操作 [Queryable(AllowedQueryOptions=AllowedQueryOptions.Skip | AllowedQueryOptions.Top)] //在指定属性上进行排序,对于在数据库中没有索引的字段会禁止排序操作的 [Queryable(AllowedOrderByProperties="Id")] //仅允许“eq”这种逻辑操作 [Queryable(AllowedLogicalOperators=AllowedLogicalOperators.Equal)] //禁止所有的算术操作 [Queryable(AllowedArithmeticOperators=AllowedArithmeticOperators.None)]
可以通过构造一个QueryableAttribute的实例在定义全局查询过滤
var queryAttribute = new QueryableAttribute() { AllowedQueryOptions = AllowedQueryOptions.Top | AllowedQueryOptions.Skip, MaxTop = 100 }; config.EnableQuerySupport(queryAttribute);
直接调用查询选项
不使用[Queryable]特性,可以在controller中直接调用查询选项,这要做需要在方法中添加一个ODataQueryOptions参数
public IQueryable<Product> Get(ODataQueryOptions opts) { var settings = new ODataValidationSettings() { // Initialize settings as needed. AllowedFunctions = AllowedFunctions.AllMathFunctions }; opts.Validate(settings); IQueryable results = opts.ApplyTo(products.AsQueryable()); return results as IQueryable<Product>; }
查询验证
[Querable]特性在执行之前就验证查询选项,验证步骤在QueryableAttribute.ValidateQuery 方法中执行,可以自定义验证过程。如下所示:

1 public class MyOrderByValidator : OrderByQueryValidator 2 { 3 // Disallow the 'desc' parameter for $orderby option. 4 public override void Validate(OrderByQueryOption orderByOption, 5 ODataValidationSettings validationSettings) 6 { 7 if (orderByOption.OrderByNodes.Any( 8 node => node.Direction == OrderByDirection.Descending)) 9 { 10 throw new ODataException("The 'desc' option is not supported."); 11 } 12 base.Validate(orderByOption, validationSettings); 13 } 14 } 15 16 public class MyQueryableAttribute : QueryableAttribute 17 { 18 public override void ValidateQuery(HttpRequestMessage request, 19 ODataQueryOptions queryOptions) 20 { 21 if (queryOptions.OrderBy != null) 22 { 23 queryOptions.OrderBy.Validator = new MyOrderByValidator(); 24 } 25 base.ValidateQuery(request, queryOptions); 26 } 27 } 28 29 // Globally: 30 config.EnableQuerySupport(new MyQueryableAttribute()); 31 32 // Per controller: 33 public class ValuesController : ApiController 34 { 35 [MyQueryable] 36 public IQueryable<Product> Get() 37 { 38 return products.AsQueryable(); 39 } 40 }
如果直接使用ODataQueryOptions 可以设置验证器
public IQueryable<Product> Get(ODataQueryOptions opts) { if (opts.OrderBy != null) { opts.OrderBy.Validator = new MyOrderByValidator(); } var settings = new ODataValidationSettings() { // Initialize settings as needed. AllowedFunctions = AllowedFunctions.AllMathFunctions }; // Validate opts.Validate(settings); IQueryable results = opts.ApplyTo(products.AsQueryable()); return results as IQueryable<Product>; }
Note:在新版本的WebAPI中[Queryable]特性已经被否决,使用[EnableQuery]特性