本文屬於 OData 系列文章
引言
OData 是一個開放標準,已經在 oasis 組織標準化,因此我們可以在標準的官網查詢到 OData 的標準請求與返回形式:OData JSON Format Version 4.01 (oasis-open.org)
針對不同的資料型別,輸出返回的格式也不盡相同,涉及的內容非常多。日常使用 OData 的過程中,我們經常處理的是實體物件以及實體物件的集合。如果直接返回 IQueryable
用於 OData 查詢,那麼返回的資料大多是集合(陣列/列表)。
{
"@odata.context": "http://localhost:9000/api/v2/$metadata#Collection(Datum_AggDto)",
"@odata.count": 2,
"value": [
{
"timestamp": 1682294400000,
"max": 180.0,
"min": 152.0,
"avg": 161.7605633802817
},
{
"timestamp": 1682985600000,
"max": 281.0,
"min": 180.0,
"avg": 228.39583333333334
}]
}
這個陣列物件也不是很純粹,它被 value 封裝,並且提供了 @odata.context
後設資料連結。如果我們的 API 沒有被 OData 路由解析,那麼預設 WEBAPI 會返回一個純粹的陣列物件:
[
{
"timestamp": 1682294400000,
"max": 180.0,
"min": 152.0,
"avg": 161.7605633802817
},
{
"timestamp": 1682985600000,
"max": 281.0,
"min": 180.0,
"avg": 228.39583333333334
}]
假設我們的對外的資料介面不完全被 OData 路由,會導致前端訪問的行為不一致:一些 API 可以直接解析,另外一些 API 則需要使用 value 封裝後處理。
封裝非 OData Route Mapping
由於 OData 有了標準,為了對外保持一致性,我們可以嘗試在返回非 OData 路由 API 時,將原始陣列物件進行封裝。
單實體物件
[HttpGet("/api/v1/Current")]
[ProducesResponseType(typeof(DeviceDataDto), Status200OK)]
public IActionResult Current(string key)
{
key = key.Trim('\'');
var data = _context.DeviceData.Where(w => w.DeviceId == key).OrderByDescending(w => w.Timestamp).FirstOrDefault();
return Ok(_mapper.Map<DeviceDataDto>(datas));
}
對於以上的程式碼,只返回單個實體物件,返回的形式與 OData 標準中返回單個實體物件的標準一致,因此不需要額外的轉換操作。
{
//OData 返回會多一個context,普通API不會有。
"@odata.context": "http://localhost:9000/api/v2/$metadata#Datum_AggDto",
"timestamp": 1682985600000,
"max": 281.0,
"min": 180.0,
"avg": 228.39583333333334
}
實體物件集合
[HttpGet("/api/v1")]
[ProducesResponseType(typeof(IEnumerable<DeviceDataDto>), Status200OK)]
public async Task<IActionResult> Get(string key)
{
key = key.Trim('\'');
return Ok(await _context.DeviceData.Where(w => w.DeviceId == key).ToListAsync());
}
以上程式碼返回的型別是一個集合,並且被 OData 路由對映。我們使用 value 這個 key 對齊進行封裝:
[HttpGet("/api/v1")]
[ProducesResponseType(typeof(IEnumerable<DeviceDataDto>), Status200OK)]
public async Task<IActionResult> Get(string key)
{
key = key.Trim('\'');
var datas = await _context.DeviceData.Where(w => w.DeviceId == key).ToListAsync();
var result = new { Value = datas };
return Ok(result);
}
注意這個 Value
我使用的是大寫,由於我啟用了 camelCase
,所以會自動轉換為小寫。這樣前端訪問 API 時,不論是否為 OData API 都可以訪問 value 的值獲取陣列物件。