ASP.NET Web API 2'de OData Sorgu Seçeneklerini Destekleme

tarafından Mike Wasson

Kod örnekleriyle birlikte bu genel bakış, ASP.NET 4.x için ASP.NET Web API 2'de desteklenen OData Sorgu Seçeneklerini gösterir.

OData, OData sorgusunu değiştirmek için kullanılabilecek parametreleri tanımlar. İstemci bu parametreleri istek URI'sinin sorgu dizesinde gönderir. Örneğin, sonuçları sıralamak için istemci $orderby parametresini kullanır:

http://localhost/Products?$orderby=Name

OData belirtimi bu parametre sorgu seçeneklerini çağırır. Projenizdeki herhangi bir Web API denetleyicisi için OData sorgu seçeneklerini etkinleştirebilirsiniz; denetleyicinin OData uç noktası olması gerekmez. Bu, herhangi bir Web API'sine filtreleme ve sıralama gibi özellikleri eklemek için kullanışlı bir yol sağlar.

Sorgu seçeneklerini etkinleştirmeden önce lütfen OData Güvenlik Kılavuzu konusunu okuyun.

OData Sorgu Seçeneklerini Etkinleştirme

Web API'si aşağıdaki OData sorgu seçeneklerini destekler:

Seçenek Açıklama
$expand İlgili varlıkları satır içi olarak genişletir.
$filter Boole koşuluna göre sonuçları filtreler.
$inlinecount Sunucuya yanıta eşleşen varlıkların toplam sayısını eklemesini söyler. (Sunucu tarafı disk belleği için kullanışlıdır.)
$orderby Sonuçları sıralar.
$select Yanıta eklenecek özellikleri seçer.
$skip İlk n sonucu atlar.
$top Yalnızca ilk n sonucu döndürür.

OData sorgu seçeneklerini kullanmak için bunları açıkça etkinleştirmeniz gerekir. Bunları tüm uygulama için genel olarak etkinleştirebilir veya belirli denetleyiciler veya belirli eylemler için etkinleştirebilirsiniz.

OData sorgu seçeneklerini genel olarak etkinleştirmek için başlangıçta HttpConfiguration sınıfında EnableQuerySupport'u çağırın:

public static void Register(HttpConfiguration config)
{
    // ...

    config.EnableQuerySupport();

    // ...
}

EnableQuerySupport yöntemi, IQueryable türü döndüren tüm denetleyici eylemleri için genel olarak sorgu seçeneklerini etkinleştirir. Tüm uygulama için sorgu seçeneklerinin etkinleştirilmesini istemiyorsanız, eylem yöntemine [Queryable] özniteliğini ekleyerek bunları belirli denetleyici eylemleri için etkinleştirebilirsiniz.

public class ProductsController : ApiController
{
    [Queryable]
    IQueryable<Product> Get() {}
}

Örnek Sorgular

Bu bölümde, OData sorgu seçenekleri kullanılarak mümkün olan sorgu türleri gösterilir. Sorgu seçenekleri hakkında ayrıntılı bilgi için www.odata.org'daki OData belgelerine bakın.

$expand ve $select hakkında bilgi için bkz. ASP.NET Web API OData'da $select, $expand ve $value kullanma.

İstemci Temelli Sayfalama

Büyük varlık kümeleri için istemci sonuç sayısını sınırlamak isteyebilir. Örneğin, bir istemci bir kerede 10 giriş gösterebilir ve bir sonraki sonuç sayfasını almak için "sonraki" bağlantıları olabilir. Bunu yapmak için istemci $top ve $skip seçeneklerini kullanır.

http://localhost/Products?$top=10&$skip=20

$top seçeneği döndürülecek en fazla girdi sayısını, $skip seçeneği ise atacak girdi sayısını verir. Önceki örnek, 21 ile 30 arasında girişleri getirir.

Filtreleme

$filter seçeneği, bir istemcinin Boole ifadesi uygulayarak sonuçları filtrelemesine olanak tanır. Filtre ifadeleri oldukça güçlü; mantıksal ve aritmetik işleçler, dize işlevleri ve tarih işlevleri içerir.

"Toys" kategorisine eşit tüm ürünleri iade edin. http://localhost/Products?$filter=Category eq 'Toys'
Fiyatı 10'dan az olan tüm ürünleri iade edin. http://localhost/Products?$filter=Price lt 10
Mantıksal işleçler: Fiyat = 5 ve fiyat ><= 15 olan tüm ürünleri döndürür. http://localhost/Products?$filter=Price ge 5 ve Price le 15
Dize işlevleri: Adında "zz" bulunan tüm ürünleri döndürür. http://localhost/Products?$filter=substringof('zz',Name)
Tarih işlevleri: 2005'in ardından ReleaseDate ile tüm ürünleri iade edin. http://localhost/Products?$filter=year(ReleaseDate) gt 2005

Sıralama

Sonuçları sıralamak için $orderby filtresini kullanın.

Fiyata göre sırala. http://localhost/Products?$orderby=Price
Fiyata göre azalan düzende sıralayın (en yüksek ile en düşük arasında). http://localhost/Products?$orderby=Price desc
Kategoriye göre sıralayın, ardından kategoriler içinde fiyata göre azalan düzende sıralayın. http://localhost/odata/Products?$orderby=Category,Price desc

Sayfalama Server-Driven

Veritabanınızda milyonlarca kayıt varsa hepsini tek bir yükte göndermek istemezsiniz. Bunu önlemek için, sunucu tek bir yanıtta gönderdiği girdi sayısını sınırlayabilir. Sunucu sayfalama özelliğini etkinleştirmek için Queryable özniteliğinde PageSize özelliğini ayarlayın. Değer, döndürülecek en fazla girdi sayısıdır.

[Queryable(PageSize=10)]
public IQueryable<Product> Get() 
{
    return products.AsQueryable();
}

Denetleyiciniz OData biçimini döndürürse, yanıt gövdesi bir sonraki veri sayfasına bağlantı içerir:

{
  "odata.metadata":"http://localhost/$metadata#Products",
  "value":[
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
    // Others not shown
  ],
  "odata.nextLink":"http://localhost/Products?$skip=10"
}

İstemci, sonraki sayfayı getirmek için bu bağlantıyı kullanabilir. Sonuç kümesindeki toplam girdi sayısını öğrenmek için istemci $inlinecount sorgu seçeneğini "allpages" değeriyle ayarlayabilir.

http://localhost/Products?$inlinecount=allpages

"allpages" değeri sunucuya yanıta toplam sayıyı eklemesini söyler:

{
  "odata.metadata":"http://localhost/$metadata#Products",
  "odata.count":"50",
  "value":[
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
    // Others not shown
  ]
}

Not

Sonraki sayfa bağlantıları ve satır içi sayıların her ikisi de OData biçimi gerektirir. Bunun nedeni, OData'nın bağlantıyı ve sayıyı tutmak için yanıt gövdesinde özel alanlar tanımlamasıdır.

OData olmayan biçimler için, sorgu sonuçlarını PageResult<T> nesnesine kaydırarak sonraki sayfa bağlantılarını ve satır içi sayıyı desteklemeye devam etmek mümkündür. Ancak, biraz daha fazla kod gerektirir. Örnek aşağıda verilmiştir:

public PageResult<Product> Get(ODataQueryOptions<Product> options)
{
    ODataQuerySettings settings = new ODataQuerySettings()
    {
        PageSize = 5
    };

    IQueryable results = options.ApplyTo(_products.AsQueryable(), settings);

    return new PageResult<Product>(
        results as IEnumerable<Product>, 
        Request.GetNextPageLink(), 
        Request.GetInlineCount());
}

Aşağıda örnek bir JSON yanıtı verilmiştir:

{
  "Items": [
    { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
    { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },

    // Others not shown
    
  ],
  "NextPageLink": "http://localhost/api/values?$inlinecount=allpages&$skip=10",
  "Count": 50
}

Sorgu Seçeneklerini Sınırlama

Sorgu seçenekleri, istemciye sunucuda çalıştırılacak sorgu üzerinde çok fazla denetim sağlar. Bazı durumlarda, güvenlik veya performans nedenleriyle kullanılabilir seçenekleri sınırlamak isteyebilirsiniz. [Queryable] özniteliği bunun için bazı yerleşik özelliklere sahiptir. Aşağıda bazı örnekler verilmiştir.

Disk belleğini desteklemek için yalnızca $skip ve $top izin verin ve başka hiçbir şey yok:

[Queryable(AllowedQueryOptions=
    AllowedQueryOptions.Skip | AllowedQueryOptions.Top)]

Veritabanında dizine alınmayan özelliklerde sıralamayı önlemek için yalnızca belirli özelliklere göre sıralamaya izin verin:

[Queryable(AllowedOrderByProperties="Id")] // comma-separated list of properties

"eq" mantıksal işlevine izin verin, ancak başka mantıksal işlev yok:

[Queryable(AllowedLogicalOperators=AllowedLogicalOperators.Equal)]

Aritmetik işleçlere izin verme:

[Queryable(AllowedArithmeticOperators=AllowedArithmeticOperators.None)]

QueryableAttribute örneği oluşturup EnableQuerySupport işlevine geçirerek seçenekleri genel olarak kısıtlayabilirsiniz:

var queryAttribute = new QueryableAttribute()
{
    AllowedQueryOptions = AllowedQueryOptions.Top | AllowedQueryOptions.Skip,
    MaxTop = 100
};
                
config.EnableQuerySupport(queryAttribute);

Sorgu Seçeneklerini Doğrudan Çağırma

[Queryable] özniteliğini kullanmak yerine sorgu seçeneklerini doğrudan denetleyicinizde çağırabilirsiniz. Bunu yapmak için denetleyici yöntemine bir ODataQueryOptions parametresi ekleyin. Bu durumda ,[Queryable] özniteliğine ihtiyacınız yoktur.

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>;
}

Web API,URI sorgu dizesinden ODataQueryOptions'ı doldurur. Sorguyu uygulamak için ApplyTo yöntemine bir IQueryable geçirin. yöntemi başka bir IQueryable döndürür.

Gelişmiş senaryolar için, IQueryable sorgu sağlayıcınız yoksa ODataQueryOptions'ı inceleyebilir ve sorgu seçeneklerini başka bir forma çevirebilirsiniz. (Örneğin, RaghuRam Nadiminti'nin OData sorgularını HQL'ye çevirme blog gönderisine bakın)

Sorgu Doğrulama

[Queryable] özniteliği sorguyu yürütmeden önce doğrular. Doğrulama adımı QueryableAttribute.ValidateQuery yönteminde gerçekleştirilir. Doğrulama işlemini de özelleştirebilirsiniz.

Ayrıca bkz. OData Güvenlik Kılavuzu.

İlk olarak, Web.Http.OData.Query.Validators ad alanında tanımlanan doğrulayıcı sınıflarından birini geçersiz kılın. Örneğin, aşağıdaki doğrulayıcı sınıfı $orderby seçeneği için 'desc' seçeneğini devre dışı bırakır.

public class MyOrderByValidator : OrderByQueryValidator
{
    // Disallow the 'desc' parameter for $orderby option.
    public override void Validate(OrderByQueryOption orderByOption,
                                    ODataValidationSettings validationSettings)
    {
        if (orderByOption.OrderByNodes.Any(
                node => node.Direction == OrderByDirection.Descending))
        {
            throw new ODataException("The 'desc' option is not supported.");
        }
        base.Validate(orderByOption, validationSettings);
    }
}

ValidateQuery yöntemini geçersiz kılmak için [Queryable] özniteliğini alt sınıfa ekleyin.

public class MyQueryableAttribute : QueryableAttribute
{
    public override void ValidateQuery(HttpRequestMessage request, 
        ODataQueryOptions queryOptions)
    {
        if (queryOptions.OrderBy != null)
        {
            queryOptions.OrderBy.Validator = new MyOrderByValidator();
        }
        base.ValidateQuery(request, queryOptions);
    }
}

Ardından özel özniteliğinizi genel olarak veya denetleyici başına ayarlayın:

// Globally:
config.EnableQuerySupport(new MyQueryableAttribute());

// Per controller:
public class ValuesController : ApiController
{
    [MyQueryable]
    public IQueryable<Product> Get()
    {
        return products.AsQueryable();
    }
}

ODataQueryOptions'ı doğrudan kullanıyorsanız, seçeneklerde doğrulayıcıyı ayarlayın:

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>;
}