| | |
| | | using Consul; |
| | | using Azure; |
| | | using Consul; |
| | | using Furion; |
| | | using Furion.DatabaseAccessor; |
| | | using Furion.FriendlyException; |
| | | using Furion.HttpRemote; |
| | | using Microsoft.EntityFrameworkCore; |
| | | using Microsoft.Extensions.Caching.Distributed; |
| | | using RabbitMQ.Client; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel.Design; |
| | | using System.Linq; |
| | | using System.Net.Http; |
| | | using System.Reflection; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace FlexJobApi.Core |
| | | { |
| | | public class ResourceHttpUtils |
| | | public static class ResourceHttpUtils |
| | | { |
| | | private readonly IDistributedCache distributedCache; |
| | | private readonly IHttpRemoteService httpRemoteService; |
| | | private readonly IConsulClient consulClient; |
| | | |
| | | public ResourceHttpUtils( |
| | | IDistributedCache distributedCache, |
| | | IHttpRemoteService httpRemoteService, |
| | | IConsulClient consulClient) |
| | | public static async Task<string> SendHttpAsync(this object request, string serviceName, string route, EnumResourceMethod method, EnumResourceHttpProvider? provider = null) |
| | | { |
| | | this.distributedCache = distributedCache; |
| | | this.httpRemoteService = httpRemoteService; |
| | | this.consulClient = consulClient; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 发送HTTP请求 |
| | | /// </summary> |
| | | /// <typeparam name="TRequest"></typeparam> |
| | | /// <typeparam name="TResponse"></typeparam> |
| | | /// <param name="request"></param> |
| | | /// <param name="provider"></param> |
| | | /// <returns></returns> |
| | | public async Task<TResponse> SendHttpAsync<TRequest, TResponse>( |
| | | TRequest request, |
| | | IResourceHttpProvider provider = null) |
| | | where TRequest : class, new() |
| | | { |
| | | var requestTypeFullName = typeof(TRequest).FullName; |
| | | |
| | | var jsonResourceModels = await distributedCache.GetStringAsync($"ResourceModel|{requestTypeFullName}"); |
| | | var resource = jsonResourceModels.JsonTo<ResourceModel>(); |
| | | var domain = await GetHealthyServiceDomain(resource); |
| | | var builder = HttpRequestBuilder.Create(resource.GetHttpMethod(), $"{domain}{resource.Route}"); |
| | | if (resource.Method == EnumResourceMethod.Get) |
| | | serviceName = App.GetConfig<string>($"{serviceName}:ServiceName"); |
| | | var url = await GetUrl(serviceName, route); |
| | | var builder = HttpRequestBuilder.Create(method.GetMethod(), url); |
| | | if (method == EnumResourceMethod.Get) |
| | | builder = builder.WithQueryParameters(request); |
| | | else |
| | | builder = builder.SetJsonContent(request); |
| | | provider = provider ?? new DefaultResourceHttpProvider(); |
| | | builder = provider.AddAuthentication(builder); |
| | | var response = await provider.SendAsAsync<TResponse>(httpRemoteService, builder); |
| | | IResourceHttpProvider httpProvider = |
| | | provider == EnumResourceHttpProvider.SystemUser |
| | | ? new SystemUserResourceHttpProvider() |
| | | : provider == EnumResourceHttpProvider.ElectronSignServer |
| | | ? new ElectronSignServerResourceHttpProvider() |
| | | : provider == EnumResourceHttpProvider.ElectronSignServerCustomer |
| | | ? new ElectronSignServerCustomerResourceHttpProvider() |
| | | : new DefaultResourceHttpProvider(); |
| | | var response = await httpProvider.SendAsStringAsync(builder); |
| | | return response; |
| | | } |
| | | |
| | | public static async Task<TResponse> SendHttpAsync<TRequest, TResponse>(this TRequest request, EnumResourceHttpProvider? provider = null) |
| | | { |
| | | var requestType = typeof(TRequest); |
| | | var resourceAttribute = requestType.GetCustomAttribute<ResourceAttribute>(); |
| | | if (resourceAttribute == null) throw Oops.Oh(EnumErrorCodeType.s400, "缺失资源特性"); |
| | | var controller = resourceAttribute.Controllers.FirstOrDefault(); |
| | | var resourceControllerAttribute = controller.GetCustomAttribute<EnumResourceController, ResourceControllerAttribute>(); |
| | | if (resourceAttribute == null) throw Oops.Oh(EnumErrorCodeType.s400, "缺失控制器特性"); |
| | | var serviceName = resourceControllerAttribute.Service.ToString(); |
| | | var route = resourceAttribute.Route; |
| | | var method = resourceAttribute.Method; |
| | | var requestXmlDoc = await requestType.GetXmlDocMemberAsync(); |
| | | if (route.IsNull() || method == EnumResourceMethod.None) |
| | | { |
| | | var resource = await Db.GetRepository<Resource>().AsQueryable().AsNoTracking() |
| | | .Where(it => |
| | | it.ServiceName == serviceName |
| | | && it.ControllerName == resourceControllerAttribute.ControllerName |
| | | && it.Code == requestXmlDoc.Name) |
| | | .Select(it => new |
| | | { |
| | | it.Route, |
| | | it.Method |
| | | }) |
| | | .FirstOrDefaultAsync(); |
| | | if (resource == null) throw Oops.Oh(EnumErrorCodeType.s404, "资源"); |
| | | route = resource.Route; |
| | | method = resource.Method; |
| | | } |
| | | var responseJson = await request.SendHttpAsync(serviceName, route, method, provider); |
| | | var response = responseJson.JsonTo<TResponse>(); |
| | | return response; |
| | | } |
| | | |
| | | public static HttpMethod GetMethod(this EnumResourceMethod method) |
| | | { |
| | | switch (method) |
| | | { |
| | | case EnumResourceMethod.Get: |
| | | return HttpMethod.Get; |
| | | case EnumResourceMethod.Post: |
| | | return HttpMethod.Post; |
| | | case EnumResourceMethod.Put: |
| | | return HttpMethod.Put; |
| | | case EnumResourceMethod.Delete: |
| | | return HttpMethod.Delete; |
| | | default: |
| | | throw Oops.Oh(EnumErrorCodeType.s400, "不支持的请求类型"); |
| | | } |
| | | } |
| | | |
| | | public static async Task<string> GetUrl(string serviceName, string route) |
| | | { |
| | | var domain = await GetHealthyServiceDomain(serviceName); |
| | | return $"{domain}{route}"; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取健康服务域名 |
| | | /// </summary> |
| | | /// <param name="resource"></param> |
| | | /// <param name="serviceName"></param> |
| | | /// <returns></returns> |
| | | public async Task<string> GetHealthyServiceDomain(ResourceModel resource) |
| | | public static async Task<string> GetHealthyServiceDomain(string serviceName) |
| | | { |
| | | var queryResult = await consulClient.Health.Service(resource.ServiceName, null, true); |
| | | var env = App.GetConfig<string>("Environment"); |
| | | serviceName = $"{env}_{serviceName}"; |
| | | |
| | | var queryResult = await App.GetRequiredService<IConsulClient>().Health.Service(serviceName, null, true); |
| | | if (queryResult.StatusCode != System.Net.HttpStatusCode.OK) |
| | | throw Oops.Oh(EnumErrorCodeType.s404, $"微服务{resource.Service}"); |
| | | throw Oops.Oh(EnumErrorCodeType.s404, $"微服务{serviceName}"); |
| | | |
| | | var domains = queryResult.Response |
| | | .Select(s => $"http://{s.Service.Address}:{s.Service.Port}") |
| | | .ToList(); |
| | | if (domains.IsNull()) |
| | | throw Oops.Oh(EnumErrorCodeType.s404, $"微服务{resource.Service}"); |
| | | throw Oops.Oh(EnumErrorCodeType.s404, $"微服务{serviceName}"); |
| | | // 轮询选择实例 |
| | | int randomIndex = new Random().Next(domains.Count); |
| | | return domains[randomIndex]; |