OData WebAPI實踐-與ABP vNext集成

本文屬於 OData 系列文章


ABP 是一個流行的 ASP. NET 開發框架,舊版的的 ABP 已經能夠非常好的支持了 OData ,並提供了對應的 OData 包。

ABP vNext 是一個重新設計的,面向微服務的框架,提供了一些非常有用的特性,包括分頁查詢等但是它並不能原生支持 OData ,我們需要自行實現。

本文的實現方式本質上爲 side by side 方式,由於 ABP vNext 官方沒有對應的設計,所以你依然需要自己編寫控制器。

本文使用 ABP CLI 6.0.3 生成的 ABP vNext 項目、Microsoft.AspNetCore.OData 8.1.2

原理

ABP vNext 有自動生成 Controller 的機制,我們的 ApplicationService,它會幫我們自動生成對應的終結點,並對外提供服務。這個過程是由 ABP 控制的,我們能修改的內容非常有限。

	// TodoAppHttpApiHostModule.cs
    private void ConfigureConventionalControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(TodoAppApplicationModule).Assembly, options =>
                {
                    //// 移除自動生成的控制器
                    //options.ControllerTypes.Remove(typeof(TodoAppController));
                    //options.RootPath = "abp";
                    //// 添加手動實現的控制器
                    //options.ControllerTypes.Add(typeof(TodoAppImplController));
                });
        });
    }

它依照慣例生成,我們可以對類型進行增加或者減少,但是不能控制生成的行爲。而 OData 的控制器,需要我們從 ODataController 繼承,或者使用 [EnableQuery],因此我們需要阻止自動生成控制器,自己實現對應的邏輯。

有了對應的控制器,就需要在應用程序模塊中配置 OData 的路由、EDM 等信息,並且不要和原有的控制器路由衝突。

實現

首先禁用自動生成控制器功能。

	//TodoAppService.cs
    [RemoteService(false)]
    public class TodoAppService : ApplicationService, ITodoAppService
    {
        private readonly IRepository<TodoItem, Guid> _todoItemRepository;

        public TodoAppService(IRepository<TodoItem, Guid> todoItemRepository)
        {
            _todoItemRepository = todoItemRepository;
        }

        public async Task<IQueryable<TodoItemDto>> GetListAsync()
        {
            var items = await _todoItemRepository.GetQueryableAsync();
            return items
        .Select(item => new TodoItemDto
        {
            Id = item.Id,
            Text = item.Text
        })
        }
    }

將應用服務打上 [RemoteService(false)] 標記,服務就不會自動生成控制器。接下來在 HttpApi 項目中新建一個與服務同名的控制器,由於 ABP 項目自動生成了控制器抽象類模板,我們繼承並實現它即可。

/* Inherit your controllers from this class.
 */
public abstract class TodoAppController : AbpControllerBase
{
    protected TodoAppController()
    {
        LocalizationResource = typeof(TodoAppResource);
    }
}


public class TodoController : TodoAppController
{
    private readonly ITodoAppService todoAppService;

    public TodoController(ITodoAppService todoAppService)
       :base() 
    {
        this.todoAppService = todoAppService;
    }

    [EnableQuery]
    public async Task<IEnumerable<TodoItemDto>> GetAsync()
    {
        var result = await todoAppService.GetListAsync();
        return result.AsQueryable();
    }
}

注入對應的服務,實現自己的邏輯,如果需要利用 EF Core 的查詢功能,請使用 IQueryable 傳遞數據到控制器層。然後配置 OData :

	//TodoAppHttpApiHostModule.cs
    private IEdmModel GetEdmModels()
    {
        var builder = new ODataConventionModelBuilder();
        var device = builder.EntitySet<TodoItemDto>("Todo").EntityType.HasKey(w => w.Id);

        return builder.GetEdmModel();
    }

記得在 PreConfigureServices 中加上調用。

	//TodoAppHttpApiHostModule.cs
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure<OpenIddictBuilder>(builder =>
        {
            builder.AddValidation(options =>
            {
                options.AddAudiences("TodoApp");
                options.UseLocalServer();
                options.UseAspNetCore();
            });
        });
        // 加上這個調用
        PreConfigure<IMvcBuilder>(builder =>
        {
            builder.AddOData(opt =>
            {
                opt.RouteOptions.EnablePropertyNameCaseInsensitive = true;
                opt.RouteOptions.EnableQualifiedOperationCall = false;
                opt.Expand().Filter().Count().OrderBy().Filter().SetMaxTop(30);
                opt.AddRouteComponents("api/app/", GetEdmModels());
            });
        });
    }

運行後,大功告成:

image

總結

本文實現了 OData 在 ABP vNext 中的使用。請注意,本方案只是一個 Demo,應用到生產前請自行評估風險,期待 ABP 團隊在未來正式支持 OData 吧。本文的完整代碼在 github,運行前可能需要先執行數據庫初始化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章