OData WebAPI实践-与ABP vNext集成

WebAPI,vNext · 浏览次数 : 52

小编点评

**本文介绍了如何使用 ABP vNext 实现 OData 的用法。** **主要步骤:** 1. **配置应用程序模块:** - 创建一个新的应用程序模块。 - 在模块中配置 OData 的路由、EDM 等信息。 2. **创建 OData 控制器:** - 在应用程序模块中配置 OData 的路由。 - 使用 `[EnableQuery]` 等特性启用查询功能。 3. **实现自定义逻辑:** - 在控制器中使用 `IQueryable` 返回数据。 - 在 `PreConfigureServices` 中配置 OData。 **关键代码:** ```csharp // TodoAppService.cs [RemoteService(false)] public class TodoAppService : ApplicationService, ITodoAppService { private readonly IRepository _todoItemRepository; public TodoAppService(IRepository todoItemRepository) { _todoItemRepository = todoItemRepository; } public async Task> GetListAsync() { var items = await _todoItemRepository.GetQueryableAsync(); return items.Select(item => new TodoItemDto { Id = item.Id, Text = item.Text }); } } // TodoController.cs public class TodoController : TodoAppController { private readonly ITodoAppService todoAppService; public TodoController(ITodoAppService todoAppService) { this.todoAppService = todoAppService; } [EnableQuery] public async Task> GetAsync() { var result = await todoAppService.GetListAsync(); return result.AsQueryable(); } } ``` **注意:** - 本方案只是一个 Demo,在生产前请自行评估风险。 - 本代码需要在运行之前执行数据库初始化。

正文

本文属于 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,运行前可能需要先执行数据库初始化。

与OData WebAPI实践-与ABP vNext集成相似的内容:

OData WebAPI实践-与ABP vNext集成

本文属于 OData 系列文章 ABP 是一个流行的 ASP. NET 开发框架,旧版的的 ABP 已经能够非常好的支持了 OData ,并提供了对应的 OData 包。 ABP vNext 是一个重新设计的,面向微服务的框架,提供了一些非常有用的特性,包括分页查询等但是它并不能原生支持 OData

OData WebAPI实践-OData与EDM

本文属于 OData 系列 引言 在 OData 中,EDM(Entity Data Model) 代表“实体数据模型”,它是一种用于表示 Web API 中的结构化数据的格式。EDM 定义了可以由 OData 服务公开的数据类型、实体和关系。 EDM 也提供了一些规则来描述数据模型中的实体之间的关

OData WebAPI实践-Non-EDM模式

本文属于OData系列文章 前文说到了 EDM 与 OData 之间的关系,具有 EDM 的 OData 提供了强大的查询能力,但是 OData 并不必须要配置 EDM,我们也可以使用 Non-EDM 方案。 Non-EDM 所谓 Non-EDM ,并不是说在 OData 运行时不需要 EDM 配置

OData WebAPI实践-兼容OData集合响应

本文属于 OData 系列文章 引言 OData 是一个开放标准,已经在 oasis 组织标准化,因此我们可以在标准的官网查询到 OData 的标准请求与返回形式:OData JSON Format Version 4.01 (oasis-open.org) 针对不同的数据类型,输出返回的格式也不尽

武装你的WEBAPI-OData之API版本管理

本文属于OData系列 Intro 对外提供WEBAPI时,如果遇上了版本升级,那么控制WEBAPI的版本也是非常必要的。OData官方提供了版本控制以及管理的解决方案,我个人是实践体会是不好用,好在社区提供了对应的nuget包,与.NET主版本同步更新。 介绍 ASP.NET API Versio

武装你的WEBAPI-OData与DTO

本文属于OData系列文章 Intro 前面写了很多有关OData使用的文章,很多读者会有疑问,直接将实体对象暴露给最终用户会不会有风险?$expand在默认配置的情况下,数据会不会有泄露风险? 答案是肯定的,由于OData的特性,提供给我们便捷同时也会带来一些风险。很多地方推荐使用DTO模式来隔离

武装你的WEBAPI-OData聚合查询

本文属于OData系列 Introduction ODATA v4提出了新的聚合查询功能,这对于ODATA的基本查询能力($expand等)是一个非常大的补充。ODATA支持的聚合查询功能,可以对数据进行统计分析,例如求和、平均值、最大/最小值、分组等。 聚合查询是通过$apply关键字实现的。使用

武装你的WEBAPI-OData使用Endpoint

本文属于 OData 系列文章 Introduction 更新: 由于新版的 OData 已经默认使用了 endpoint 模式(Microsoft.AspNetCore.OData 8.0.0),不再需要额外配置,本文已经过时(asp.net core 3.1)。 最近看 OData 的 devb

外键拆分手记

我习惯性使用OData,它的$expand与层级查询非常好用,这个功能非常依赖于数据库的导航属性,也就是外键结构。最近想着把一个单体的系统拆分为多个小系统,首先需要处理外键依赖的问题。 多个服务各自有各自的数据库,数据库层面并不互通,也就无法使用外键约束。 我使用EF Core来描述数据库的结构,有

ChatGPT与码农的机会

之前一篇博客已经写了有关AI在博客编写方面的优势与对未来博客的编写方面的思考。这篇文档我继续分享我在开发中的一个案例和相关的感想。 事件还原 我发现ChatGPT也可以帮助我编写OData,于是我也利用GPT帮助我编程。 OData如何将filter与apply字段联合使用?答案如下: GET /o