AbpVnext 微服务 内部网关服务通讯 动态API客户端

阅读此篇文章之前,你需要对Abpvnext 微服务样例项目有一定的熟悉度与理解度
文章并没有详细说明远程服务的配置相关,各位可自行查阅官网文档,或者相关博客,下面会给出相关url地址
https://docs.abp.io/zh-Hans/abp/latest/API/Dynamic-CSharp-API-Clients

场景:
Abpvnext 微服务样例项目的基础上变更出的
https://www.cnblogs.com/william-xu/p/12537155.html
InternalGateWay.Host (内部网关)
BaseService(基础服务 用户_角色_组织_权限等)
BusinessService (业务服务)

感谢各位先驱大佬的探索与分享 @一曲肝肠断

那么回到我们本篇博客的主题:微服务间 服务通讯时如何使用abpvnext的动态api客户端? 例如 ,子服务(BusinessService 如何调用 BaseService中我们定义的接口方法)

第一步,检查BaseService项目中是否构建了BaseService.HttpApi.Client项目,若没有,补充下
AbpVnext 微服务 内部网关服务通讯 动态API客户端_第1张图片

using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;

namespace BaseService.HttpApi.Client;

[DependsOn(
    typeof(BaseServiceApplicationContractsModule)
)]
public class BaseServiceHttpApiClientModule : AbpModule
{
    public const string RemoteServiceName = "Default";

    // 添加动态api客户端
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddHttpClientProxies(
            typeof(BaseServiceApplicationContractsModule).Assembly,
            RemoteServiceName
        );
    }
}

BaseService.Application项目中我实现了自己的UserAppService

[Authorize(IdentityPermissions.Users.Default)]
    public class UserAppService : ApplicationService, IUserAppService
    {
        protected IdentityUserManager UserManager { get; }
        protected IIdentityUserRepository UserRepository { get; }
        public IIdentityRoleRepository RoleRepository { get; }
        private readonly IRepository<Organization, Guid> _orgRepository;
        private readonly IRepository<UserJob> _userJobsRepository;
        private readonly IRepository<UserOrganization> _userOrgsRepository;

        public UserAppService(
            IdentityUserManager userManager,
            IIdentityUserRepository userRepository,
            IIdentityRoleRepository roleRepository,
            IRepository<Organization, Guid> orgRepository,
            IRepository<UserJob> userJobsRepository,
            IRepository<UserOrganization> userOrgsRepository
            )
        {
            UserManager = userManager;
            UserRepository = userRepository;
            RoleRepository = roleRepository;
            _orgRepository = orgRepository;
            _userJobsRepository = userJobsRepository;
            _userOrgsRepository = userOrgsRepository;
        }

        public async Task<PagedResultDto<BaseIdentityUserDto>> GetAll(GetBaseIdentityUsersInput input)
        {
            var orgQueryable = await _orgRepository.GetQueryableAsync();
            var userOrgQueryable = await _userOrgsRepository.GetQueryableAsync();
            if (input.OrganizationId.HasValue)
            {
                var userDbSet = await UserRepository.GetDbSetAsync();
                var org = await _orgRepository.GetAsync(input.OrganizationId.Value);
                var orgs = await (await _orgRepository.GetQueryableAsync()).Where(_ => _.CascadeId.Contains(org.CascadeId)).ToListAsync();

                var totalCount = await userOrgQueryable.Where(_ => orgs.Select(o => o.Id).Contains(_.OrganizationId))
                                                     .GroupBy(_ => _.UserId)
                                                     .LongCountAsync();

                //TODO: Redis Query
                var userIds = await userOrgQueryable.Where(_ => orgs.Select(o => o.Id).Contains(_.OrganizationId))
                                                        .Select(_ => _.UserId)
                                                        .Distinct()
                                                        .Skip(input.SkipCount)
                                                        .Take(input.MaxResultCount)
                                                        .ToListAsync();
                
                var items = await userDbSet.WhereIf(!string.IsNullOrWhiteSpace(input.Filter), _ => _.UserName.Contains(input.Filter))
                                           .Where(_ => userIds.Contains(_.Id)).ToListAsync();
                var userOrgs = await userOrgQueryable.Where(_ => items.Select(i => i.Id).Contains(_.UserId))
                                        .ToListAsync();
                var allOrg = await orgQueryable.Where(_ => userOrgs.Select(uo => uo.OrganizationId).Contains(_.Id))
                                               .OrderBy(_ => _.CascadeId)
                                               .ToListAsync();
                var dtos = ObjectMapper.Map<List<IdentityUser>, List<BaseIdentityUserDto>>(items);

                foreach (var dto in dtos)
                {
                    var oids = userOrgs.Where(_ => _.UserId == dto.Id).Select(_ => _.OrganizationId);
                    dto.OrganizationNames = string.Join(", ", allOrg.Where(_ => oids.Contains(_.Id)).Select(_ => _.Name).ToArray());
                }
                return new PagedResultDto<BaseIdentityUserDto>(totalCount, dtos);
            }
            else
            {
                var totalCount = await UserRepository.GetCountAsync(input.Filter);
                var items = await UserRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter);
                //TODO: Redis Query
                var userOrgs = await userOrgQueryable.Where(_ => items.Select(i => i.Id).Contains(_.UserId))
                                                        .ToListAsync();
                var orgs = await orgQueryable.Where(_ => userOrgs.Select(uo => uo.OrganizationId).Contains(_.Id))
                                               .OrderBy(_ => _.CascadeId)
                                               .ToListAsync();
                var dtos = ObjectMapper.Map<List<IdentityUser>, List<BaseIdentityUserDto>>(items);
                foreach (var dto in dtos)
                {
                    var oids = userOrgs.Where(_ => _.UserId == dto.Id).Select(_ => _.OrganizationId);
                    dto.OrganizationNames = string.Join(", ", orgs.Where(_ => oids.Contains(_.Id)).Select(_ => _.Name).ToArray());
                }
                return new PagedResultDto<BaseIdentityUserDto>(totalCount, dtos);
            }
        }

    }

然后在我们的需要用到BaseService方法的项目中引入HttpApi.Client项目

(大佬是推荐构建私有nuget包管理,然后引入,个人猜测是避免项目间循环引用,同时加强了版本管理,我呢,偷懒了,直接项目引用的)

例如Business.Applicattion中想要使用BaseService 的方法GetAll获取用户分页数据(我随便写的方法,不要在意方法名,他就是拿来取分页数据的,我懒得改动而已,你可以将他看为GetPage) ---- 0.0

首先我们直接在Business.Application项目中引用BaseService.HttpApi.Client项目
然后像我们注入自己项目的接口一样,直接注入BaseService相关的接口

    public class BusinessTestService: ApplicationServiceBase
    {
       //内部网关通讯测试
        private readonly IUserAppService _userBaseAppService;

        public BusinessTestService(IUserAppService userBaseAppService)
        {
            _userBaseAppService = userBaseAppService;
        }
        /// 
        /// 内部网关通讯测试
        /// 
        /// 
        public async Task<PageResultDto<BaseIdentityUserDto>> TestInternalGateWay()
        {
            var pageParams = new GetBaseIdentityUsersInput
            {
                Filter = null,
            };
            var users = await _userBaseAppService.GetAll(pageParams);
            return users;
        }

按照官方文档的动态API客户端的说明,编码工作已经完成了
但是你会得到
Could not found remote action的奇怪错误
我们应该猜测,按照官方文档的,是直接访问相关项目部署地址的接口,而微服务项目是通过内部网关 InternalGateway 转发的相关请求,
故此时, 我们应该在InternalGateWay 内部网关项目中,引用 BaseService.HttpApi 项目(此处单纯的就是为网关提供接口定义,根据转发规则,相关接口请求会实际转发至你配置的子服务端去)
此时,再次启动调试,你会发现,已经能够正常获取到数据了
其实,动态Api客户端的设计,真的是非常贴合.netcore开发,大佬们是真的花了心思实现的这一块,极大的提高了开发效率.再次感谢前辈们的付出与分享.希望更多的人一起学习netcore… 也希望有朝一日,我们国人也能出一套自己的强大的开发框架

你可能感兴趣的:(微服务,.netcore)