本文屬於OData系列文章 Intro 前面寫了很多有關OData使用的文章,很多讀者會有疑問,直接將實體對象暴露給最終用戶會不會有風險?$expand在預設配置的情況下,數據會不會有泄露風險? 答案是肯定的,由於OData的特性,提供給我們便捷同時也會帶來一些風險。很多地方推薦使用DTO模式來隔離 ...
本文屬於OData系列文章
Intro
前面寫了很多有關OData使用的文章,很多讀者會有疑問,直接將實體對象暴露給最終用戶會不會有風險?$expand在預設配置的情況下,數據會不會有泄露風險?
答案是肯定的,由於OData的特性,提供給我們便捷同時也會帶來一些風險。很多地方推薦使用DTO模式來隔離實體類與最終用戶使用到類的關係,從而解決以上兩個問題,OData同樣也適用。
DTO
DTO代表Data Transfer Object
,是一種設計模式,用於在不同層之間傳輸數據。它通常用於將數據從一個應用程式的邏輯層傳輸到另一個應用程式的界面層或持久化層,以及在分散式系統中傳輸數據。
DTO對象是純數據對象,它包含要從一個應用程式傳輸到另一個應用程式的數據。它不包含業務邏輯或數據訪問代碼,因此它們不能直接與資料庫交互或執行任何操作,而只是簡單地保存數據。
DTO對象通常由開發人員創建,並且可以根據需要進行擴展。它們可以包含各種屬性和方法,以提供使用方便和更好的可讀性。使用DTO對象可以降低耦合度,使不同層之間的數據傳輸更加簡單和安全。
AutoMapper
我們需要將實體對象與DTO進行轉換,對於需要轉換數量不是很多的情況,直接編寫一個轉換函數就方便了。
public static class DeviceDataExtension
{
public static DeviceDataDto ToDeviceDataDto(this Datum deviceData)
{
if (deviceData == null) return null;
DeviceDataDto deviceDataDto = new()
{
DataArray = deviceData.DataArray,
DeviceId = deviceData.DeviceId,
Timestamp = deviceData.Timestamp,
Id = Guid.NewGuid().ToString()
};
return deviceDataDto;
}
}
但是如果需要映射的屬性很多,或者有很多對象的情況,建議使用對象映射工具:AutoMapper
。基礎用法不詳細說了,講講對OData的支持。
首先安裝對OData支持的包,由於我使用預設的DI,還需要安裝DI支持的包:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Install-Package AutoMapper.AspNetCore.OData.EFCore
然後有三個要求:
- 一定要對對象聲明顯示展開(explicit expansion)。
- 調用IMapper的
GetAsync()
或者GetQueryAsync()
方法。 - 不能在Controller或者方法上使用[EnableQuery]特性:這個我熟,因為
GetQueryAsync()
函數需要利用ODataQueryOptions
參數,如果同時使用[EnableQuery]
會導致對結果再進行一次篩選,導致返回數據錯誤。
代碼:
services.AddAutoMapper(option =>
{
option.CreateMap<Datum, DeviceDataDto>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.NewGuid().ToString()))
.ForPath(dest => dest.DataArray, opt => opt.MapFrom(src => src.DataArray))
.ForAllMembers(w => w.ExplicitExpansion());
});
public DeviceDatasController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<DeviceDataDto>), Status200OK)]
public async Task<IActionResult> GetAsync(string key, ODataQueryOptions<DeviceDataDto> options)
{
var insp = await _context.DeviceData.Where(w => w.DeviceId == key).GetQueryAsync(_mapper, options);
return Ok(insp);
}
這樣,我們就可以正常使用 OData,同時也享受了的 DTO 的好處,即可以對 DeviceDataDto 使用的 OData 查詢。使用的時候要註意,如果有導航屬性,導航屬性也需要配置映射。