Używanie $select, $expand i $value w internetowym interfejsie API 2 OData ASP.NET
Autor: Mike Wasson
Omówienie i przykłady kodu dla opcji $expand, $select i $value w internetowym interfejsie API OData 2 dla ASP.NET 4.x. Te opcje pozwalają klientowi kontrolować reprezentację, która wraca z serwera.
- $expand powoduje, że powiązane jednostki muszą zostać uwzględnione w tekście w odpowiedzi.
- $select wybiera podzbiór właściwości do uwzględnienia w odpowiedzi.
- $value pobiera nieprzetworzone wartości właściwości.
Przykładowy schemat
W tym artykule użyję usługi OData, która definiuje trzy jednostki: Product, Supplier i Category. Każdy produkt ma jedną kategorię i jednego dostawcę.
Poniżej przedstawiono klasy języka C#, które definiują modele jednostek:
public class Supplier
{
[Key]
public string Key {get; set; }
public string Name { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category { get; set; }
[ForeignKey("Supplier")]
public string SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
}
Zwróć uwagę, że Product
klasa definiuje właściwości nawigacji dla elementu Supplier
i Category
. Klasa Category
definiuje właściwość nawigacji dla produktów w każdej kategorii.
Aby utworzyć punkt końcowy OData dla tego schematu, użyj szkieletu Visual Studio 2013, zgodnie z opisem w temacie Tworzenie punktu końcowego OData w interfejsie API sieci Web ASP.NET. Dodaj oddzielne kontrolery dla produktu, kategorii i dostawcy.
Włączanie $expand i $select
W Visual Studio 2013 szkielet OData interfejsu API sieci Web tworzy kontroler, który automatycznie obsługuje $expand i $select. Poniżej przedstawiono wymagania dotyczące obsługi $expand i $select w kontrolerze.
W przypadku kolekcji metoda kontrolera Get
musi zwrócić zapytanie IQueryable.
[Queryable]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
W przypadku pojedynczych jednostek zwróć element SingleResult<T>, gdzie T jest elementem IQueryable zawierającym zero lub jedną jednostkę.
[Queryable]
public SingleResult<Category> GetCategory([FromODataUri] int key)
{
return SingleResult.Create(db.Categories.Where(c => c.ID == key));
}
Ponadto udekoruj Get
metody za pomocą atrybutu [Queryable], jak pokazano w poprzednich fragmentach kodu. Alternatywnie wywołaj metodę EnableQuerySupport w obiekcie HttpConfiguration podczas uruchamiania . (Aby uzyskać więcej informacji, zobacz Włączanie opcji zapytania OData).
Korzystanie z $expand
Podczas wykonywania zapytań względem jednostki lub kolekcji OData domyślna odpowiedź nie obejmuje powiązanych jednostek. Na przykład poniżej przedstawiono domyślną odpowiedź dla zestawu jednostek Kategorie:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{"ID":1,"Name":"Apparel"},
{"ID":2,"Name":"Toys"}
]
}
Jak widać, odpowiedź nie zawiera żadnych produktów, mimo że jednostka Category ma link nawigacji Produkty. Jednak klient może użyć $expand, aby uzyskać listę produktów dla każdej kategorii. Opcja $expand jest w ciągu zapytania żądania:
GET http://localhost/odata/Categories?$expand=Products
Teraz serwer będzie zawierać produkty dla każdej kategorii, wbudowane z kategoriami. Oto ładunek odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories",
"value":[
{
"Products":[
{"ID":1,"Name":"Hat","Price":"15.00","CategoryId":1,"SupplierId":"CTSO"},
{"ID":2,"Name":"Scarf","Price":"12.00","CategoryId":1,"SupplierId":"CTSO"},
{"ID":3,"Name":"Socks","Price":"5.00","CategoryId":1,"SupplierId":"FBRK"}
],
"ID":1,
"Name":"Apparel"
},
{
"Products":[
{"ID":4,"Name":"Yo-yo","Price":"4.95","CategoryId":2,"SupplierId":"WING"},
{"ID":5,"Name":"Puzzle","Price":"8.00","CategoryId":2,"SupplierId":"WING"}
],
"ID":2,
"Name":"Toys"
}
]
}
Zwróć uwagę, że każdy wpis w tablicy "value" zawiera listę Products (Produkty).
Opcja $expand powoduje rozwinięcie listy właściwości nawigacji rozdzielanych przecinkami. Poniższe żądanie rozszerza zarówno kategorię, jak i dostawcę produktu.
GET http://localhost/odata/Products(1)?$expand=Category,Supplier
Oto treść odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Products/@Element",
"Category": {"ID":1,"Name":"Apparel"},
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":1,
"Name":"Hat",
"Price":"15.00",
"CategoryId":1,
"SupplierId":"CTSO"
}
Możesz rozwinąć więcej niż jeden poziom właściwości nawigacji. Poniższy przykład obejmuje wszystkie produkty dla kategorii, a także dostawcę dla każdego produktu.
GET http://localhost/odata/Categories(1)?$expand=Products/Supplier
Oto treść odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories/@Element",
"Products":[
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":1,"Name":"Hat","Price":"15.00","CategoryId":1,"SupplierId":"CTSO"
},
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"ID":2,"Name":"Scarf","Price":"12.00","CategoryId":1,"SupplierId":"CTSO"
},{
"Supplier":{
"Key":"FBRK","Name":"Fabrikam, Inc."
},"ID":3,"Name":"Socks","Price":"5.00","CategoryId":1,"SupplierId":"FBRK"
}
],"ID":1,"Name":"Apparel"
}
Domyślnie internetowy interfejs API ogranicza maksymalną głębokość rozszerzenia do 2. Uniemożliwia to klientowi wysyłanie złożonych żądań, takich jak $expand=Orders/OrderDetails/Product/Supplier/Region
, które mogą być nieefektywne w przypadku wykonywania zapytań i tworzenia dużych odpowiedzi. Aby zastąpić wartość domyślną, ustaw właściwość MaxExpansionDepth w atrybucie [Queryable].
[Queryable(MaxExpansionDepth=4)]
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Aby uzyskać więcej informacji na temat opcji $expand, zobacz Rozwiń opcję zapytania systemowego ($expand) w oficjalnej dokumentacji OData.
Korzystanie z $select
Opcja $select określa podzestaw właściwości do uwzględnienia w treści odpowiedzi. Aby na przykład uzyskać tylko nazwę i cenę każdego produktu, użyj następującego zapytania:
GET http://localhost/odata/Products?$select=Price,Name
Oto treść odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Products&$select=Price,Name",
"value":[
{"Price":"15.00","Name":"Hat"},
{"Price":"12.00","Name":"Scarf"},
{"Price":"5.00","Name":"Socks"},
{"Price":"4.95","Name":"Yo-yo"},
{"Price":"8.00","Name":"Puzzle"}
]
}
Możesz połączyć $select i $expand w tym samym zapytaniu. Pamiętaj, aby uwzględnić rozwiniętą właściwość w opcji $select. Na przykład następujące żądanie pobiera nazwę i dostawcę produktu.
GET http://localhost/odata/Products?$select=Name,Supplier&$expand=Supplier
Oto treść odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Products&$select=Name,Supplier",
"value":[
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"Name":"Hat"
},
{
"Supplier":{"Key":"CTSO","Name":"Contoso, Ltd."},
"Name":"Scarf"
},
{
"Supplier":{"Key":"FBRK","Name":"Fabrikam, Inc."},
"Name":"Socks"
},
{
"Supplier":{"Key":"WING","Name":"Wingtip Toys"},
"Name":"Yo-yo"
},
{
"Supplier":{"Key":"WING","Name":"Wingtip Toys"},
"Name":"Puzzle"
}
]
}
Możesz również wybrać właściwości w rozwiniętej właściwości. Poniższe żądanie rozwija pozycję Products (Produkty) i wybiera nazwę kategorii oraz nazwę produktu.
GET http://localhost/odata/Categories?$expand=Products&$select=Name,Products/Name
Oto treść odpowiedzi:
{
"odata.metadata":"http://localhost/odata/$metadata#Categories&$select=Name,Products/Name",
"value":[
{
"Products":[ {"Name":"Hat"},{"Name":"Scarf"},{"Name":"Socks"} ],
"Name":"Apparel"
},
{
"Products":[ {"Name":"Yo-yo"},{"Name":"Puzzle"} ],
"Name":"Toys"
}
]
}
Aby uzyskać więcej informacji na temat opcji $select, zobacz Wybieranie opcji zapytania systemowego ($select) w oficjalnej dokumentacji OData.
Pobieranie poszczególnych właściwości jednostki ($value)
Istnieją dwa sposoby uzyskiwania przez klienta OData pojedynczej właściwości z jednostki. Klient może pobrać wartość w formacie OData lub uzyskać nieprzetworzone wartości właściwości.
Następujące żądanie pobiera właściwość w formacie OData.
GET http://localhost/odata/Products(1)/Name
Oto przykładowa odpowiedź w formacie JSON:
HTTP/1.1 200 OK
Content-Type: application/json; odata=minimalmetadata; streaming=true; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 90
{
"odata.metadata":"http://localhost:14239/odata/$metadata#Edm.String",
"value":"Hat"
}
Aby uzyskać nieprzetworzona wartość właściwości, dołącz $value do identyfikatora URI:
GET http://localhost/odata/Products(1)/Name/$value
Oto odpowiedź. Zwróć uwagę, że typ zawartości to "text/plain", a nie JSON.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
DataServiceVersion: 3.0
Content-Length: 3
Hat
Aby obsługiwać te zapytania w kontrolerze OData, dodaj metodę o nazwie GetProperty
, gdzie Property
jest nazwą właściwości. Na przykład metoda pobierania właściwości Name ma nazwę GetName
. Metoda powinna zwrócić wartość tej właściwości:
public async Task<IHttpActionResult> GetName(int key)
{
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
return Ok(product.Name);
}