首页 > 其他分享 >NetCore 之 DispatchProxy

NetCore 之 DispatchProxy

时间:2023-03-04 09:45:44浏览次数:35  
标签:return string NetCore args DispatchProxy var new public

如何使用Dispatchproxy封装REST API,让API调用更简单。

1、创建HttpClientDispathProxy类继承自DispatchProxy

    public class HttpClientDispathProxy<TInterface> : DispatchProxy
    {
        public Func<string> token;
        public ApiClient client;
        protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
        {
            if (targetMethod?.ReturnType == typeof(Task))
                return this.InvokeAsync(targetMethod, args);
            if (IsGenericTask(targetMethod?.ReturnType))
            {
                var method = this.GetType().GetMethod("InvokeAsyncT", BindingFlags.NonPublic | BindingFlags.Instance);
                var methodInfo = method!.MakeGenericMethod(targetMethod?.ReturnType?.GenericTypeArguments ?? new Type[] { });
                return methodInfo.Invoke(this, new object[] { targetMethod, args }); ;
            }
            if (targetMethod?.ReturnType != typeof(void))
            {
                var response = this.SendAsync(targetMethod, args);
            
                var result  = this.client.ReadResponse(targetMethod.ReturnType, response.Result);
                return result;
            }
            return default;
        }

        protected async Task InvokeAsync(MethodInfo? method, object?[]? args)
        {
            await this.SendAsync(method, args);
        }

        protected async Task<T> InvokeAsyncT<T>(MethodInfo? method, object?[]? args)
        {
            var response = await this.SendAsync(method, args);
            var result = this.client.ReadJson<T>(response);
            return result;
        }

        protected virtual async Task<string> SendAsync(MethodInfo methodInfo, Object[] args)
        {
            var attr = methodInfo.GetCustomAttribute<ApiAttribute>();
            if (attr == null)
                throw new ArgumentNullException(nameof(attr));
            using (HttpRequestMessage request = new())
            {
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token());
                BuildHttpRequestMessage(methodInfo, args, attr, request);
                return await client.SendAsync(request);
            }
        }

        protected virtual void BuildHttpRequestMessage(MethodInfo method, object[] args, ApiAttribute attr, HttpRequestMessage request)
        {
            request.Method = _ConvertHttpMethod(attr.HttpMethod);
            var parameters = this.GetParameters(method, args);

            if (attr.HttpMethod == "POST" || attr.HttpMethod == "PUT" || attr.HttpMethod == "PTCH")
            {
                request.RequestUri = GetRequestUrl(true, attr.Url, parameters.uri);
                if (parameters.forms != null)
                {
                    JsonSerializerOptions op = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
                    var contentString = "";
                    if (parameters.forms.Count() == 1)
                    {
                        contentString = JsonSerializer.Serialize(parameters.forms.First().Value, op);
                    }
                    else if (parameters.forms.Count() > 1)
                    {
                        contentString = JsonSerializer.Serialize(parameters.forms, op);
                    }

                    if (!String.IsNullOrEmpty(contentString))
                    {
                        request.Content = new StringContent(contentString, Encoding.UTF8, "application/json");
                    }
                }
            }
            else
            {
                request.RequestUri = GetRequestUrl(false, attr.Url, parameters.uri);
            }

        }

        protected virtual Uri GetRequestUrl(Boolean isPost, String path, Dictionary<String, Object> parameters)
        {
            var url = $"{path.TrimEnd('/')}".ToLower();
            var qMark = url.IndexOf("?") == -1 ? "?" : String.Empty;
            var and = String.IsNullOrEmpty(qMark) ? "&" : String.Empty;

            if (parameters != null)
            {
                List<String> parsedParam = new();

                foreach (var kv in parameters)
                {
                    var holder = "{" + kv.Key.ToLower() + "}";
                    if (url.Contains(holder))
                    {
                        if (kv.Value == null)
                        {
                            throw new ArgumentNullException(kv.Key, "Parameter in uri can not be null.");
                        }
                        String tempValue;
                        if (kv.Value is Enum enumValue)
                        {
                            tempValue = enumValue.ToString();//enumValue.GetEnumDescription();
                        }
                        else
                            tempValue = kv.Value.ToString();
                        url = url.Replace(holder, tempValue);

                        parsedParam.Add(kv.Key);
                    }
                }

                var leftParams = parameters.Where(i => !parsedParam.Contains(i.Key));
                if (leftParams.Any())
                {
                    List<String> tempParamsList = new();
                    foreach (var item in leftParams)
                    {
                        var value = item.Value?.ToString();
                        if (string.IsNullOrEmpty(value))
                        {
                            if (item.Value is GraphQuery query)
                            {
                                if (query.Top != 0)
                                {
                                    tempParamsList.Add("$top=" + query.Top);
                                }
                                if (string.IsNullOrEmpty(query.Expand))
                                {
                                    tempParamsList.Add("$expand=" + query.Expand);
                                }
                                if (string.IsNullOrEmpty(query.Select))
                                {
                                    tempParamsList.Add("$select=" + query.Select);
                                }
                                if (string.IsNullOrEmpty(query.Orderby))
                                {
                                    tempParamsList.Add("$orderby=" + query.Orderby);
                                }
                                if (string.IsNullOrEmpty(query.Filter))
                                {
                                    tempParamsList.Add("$filter=" + query.Filter);
                                }
                                if (query.Skip != 0)
                                {
                                    tempParamsList.Add("$skip=" + query.Skip);
                                }
                                if (query.Count)
                                {
                                    tempParamsList.Add("$count=true");
                                }
                            }
                            else
                            {
                                tempParamsList.Add($"{item.Key}={HttpUtility.UrlEncode(item.Value.ToString()).Replace("+", "%20")}");
                            }
                        }
                    }
                    url = $"{url}{qMark}{and}{String.Join("&", tempParamsList.ToArray())}";
                }
            }

            return new Uri(url);
        }

        protected virtual (Dictionary<string, object> uri, Dictionary<string, object> forms) GetParameters(MethodInfo method, object[] args)
        {
            var uri = new Dictionary<string, object>();
            var forms = new Dictionary<string, object>();
            if (args != null && args.Any())
            {
                var parameters = method.GetParameters();
                foreach (var param in parameters)
                {
                    var customAttributes = param.GetCustomAttributes();
                    foreach (var customAttribute in customAttributes)
                    {
                        if (customAttribute is ApiParameterAttribute attribute)
                        {
                            if (attribute.IsRequestUri)
                                if (!string.IsNullOrEmpty(attribute.Endpoint))
                                    uri.Add(attribute.Name ?? "endpoint", new Uri(attribute.Endpoint));
                                else
                                    uri.Add(attribute.Name, args[param.Position]);
                            else
                                forms.Add(attribute.Name, args[param.Position]);
                        }
                        else
                            forms.Add(param.Name, args[param.Position]);
                    }
                }
            }
            return (uri, forms);
        }

        private static HttpMethod _ConvertHttpMethod(string method)
            => method.ToLowerInvariant() switch
            {
                "get" => HttpMethod.Get,
                "post" => HttpMethod.Post,
                "put" => HttpMethod.Put,
                "delete" => HttpMethod.Delete,
                "patch" => HttpMethod.Patch,
                _ => throw new ArgumentOutOfRangeException($"The method type is not supported. method: {method}.")
            };

        private static Boolean IsGenericTask(Type? type)
        {
            if ((type?.IsGenericType ?? false) && type?.GetGenericTypeDefinition() == typeof(Task<>))
                return true;
            return false;
        }
    }
dispachproxy

2、创建HttpClientProxy类继承自HttpClientDispathProxy,用于创建泛型Interface实例

  public class HttpClientProxy<TInterface>: HttpClientDispathProxy<TInterface>
    {
        public static TInterface Create(ApiClient client, Func<string> func)
        {
            Object proxy = Create<TInterface, HttpClientDispathProxy<TInterface>>()!;
            ((HttpClientDispathProxy<TInterface>)proxy).token = func;
            ((HttpClientDispathProxy<TInterface>)proxy).client = client;
            return (TInterface)proxy;
        }
    }
proxy

3、创建ApiClient 类调用interface实例方法

 public partial class ApiClient
    {
        public T GetApiService<T>(Func<string> token) where T : IApiService
        {
            return HttpClientProxy<T>.Create(this, token);
        }
    }
APIClient

这里指定partial class,原因是方便扩展

    public partial class ApiClient
    {
        private readonly IHttpClientFactory _httpClient;
        public ApiClient(IHttpClientFactory httpClient)
        {
            this._httpClient = httpClient;
        }

        internal async Task<string> SendAsync(HttpRequestMessage request)
        {
            using var response = await this._httpClient.CreateClient().SendAsync(request);
            var content = await response.Content.ReadAsStringAsync();
            if (response.IsSuccessStatusCode)
                return content;
            throw new ApiClientException(response.StatusCode, content);
        }

        internal Object ReadResponse(Type type, String content)
        {
            if (!string.IsNullOrEmpty(content) && type != typeof(void))
            {
                try
                {
                    return System.Text.Json.JsonSerializer.Deserialize(content, type);
                }
                catch (Exception ex)
                {
                    throw new ApiClientException(ex.Message);
                }
            }
            return default;
        }

        internal T ReadJson<T>(String content)
        {
            if(!string.IsNullOrEmpty(content))
                try
                {
                    return (T)this.ReadResponse(typeof(T), content);
                }
                catch (Exception ex)
                {
                    throw new ApiClientException(ex.Message);
                }
            return default;
        }
    }
APIClient

4、创建接口,添加对应attribute

    public interface IApiService
    {
        [Api(HttpMethod = "GET", Url = "{endpoint}WeatherForecast")]
        Task<IEnumerable<WeatherForecast>> GetAsync(
            [ApiParameter(Endpoint = "https://localhost:7101", IsRequestUri = true)] string endpoint = default
            );
    }
interface

 ApiAttribute

   [AttributeUsage(AttributeTargets.Method)]
    public class ApiAttribute : Attribute
    {
        public string HttpMethod { get; set; }

        public string Url { get; set; }
    }
api attribute

ApiParameterAttribute

    public class ApiParameterAttribute: Attribute
    {
        public string Name { get; set; }
        public bool IsRequestUri { get; set; }
        public string Endpoint { get; set; }
        public ApiParameterAttribute()
        {

        }
        public ApiParameterAttribute(string name)
        {
            Name = name;
        }
    }
api parameter attribute

5、exception

    public class ApiClientException : Exception
    {
        public HttpStatusCode StatusCode { get; set; }
        public ApiClientException(HttpStatusCode httpStatusCode, string message)
            : base(message)
        {
            StatusCode = httpStatusCode;
        }

        public ApiClientException(string message) 
            : base(message)
        { 
        }
    }
exception

6、demo controller

    [ApiExplorerSettings(GroupName = "demo1")]
    [ApiController]
    [Route("[controller]")]
    public class SwagggerDemoController : ControllerBase
    {
        private readonly ApiClient _apiClient;
        public SwagggerDemoController(ApiClient apiClient)
        {
            this._apiClient = apiClient;
        }

        [HttpGet("getdispatchproxydemo"), Authorize]
        public IEnumerable<WeatherForecast> DispatchProxyDemo(string token)
        {
            var client = _apiClient.GetApiService<IApiService>(() => token);
            var response = client.GetAsync().GetAwaiter().GetResult();
            return response;
        }
    }
controller

 7、验证,正确取到数据

 

 

Notes:这里加了authentication验证,之前blog中有提过,可以参考。

OK 搞定!

标签:return,string,NetCore,args,DispatchProxy,var,new,public
From: https://www.cnblogs.com/qindy/p/17102811.html

相关文章

  • NetCore JWT token
    在netcore中jwt使用场景很多,网上有很多的资料,这里不再累述,之后有机会会单独介绍,今天主要以实战为主。1、createjwttoken1publicinterfaceIJwtTokenService2......
  • NetCore Resource
    在netcore中resource的配置及应用1、创建resource资源文件   2、在容器中添加配置1builder.Services.AddLocalization(options=>options.ResourcesPath="......
  • .net使用DispatchProxy
    实现动态aop并注入用表达式树调用目标方法创建一个DynamicDispatchProxy类继承DispatchProxy,字典key最好是由路劲名称+方法参数组成 publicclassDynamicDispatchPr......
  • docker下netcore内存dump
    一般开发阶段可以通过visualstudio来检查程序的内存、cup等的优化问题。vs下调试=》性能探查器,这里面大有千秋。但是好多内存问题是经过时间积累下来才暴露出来的,在生产......
  • iis7上部署netcore项目的步骤
    1、安装AspNetCoreModule托管模块(选择.NetCore3.1版本)AspNetCoreModule下载地址:https://dotnet.microsoft.com/download/dotnet-core1安装下面两个文件dotnet-sdk-3......
  • 从.net Framework4.6WPF升级到.netcore3.1/net5/6/7.0版本
    因项目需要,需将.netFramework4.6WPF升级到.netcore3.1/net5.0/6.0/7.0版本,通过很多办法解决,开始搞得一头雾水。终于,找到了办法。1、首先下载upgrade-assistant工具(.net升级......
  • .NetCore自定义模板,发布Nuget
    1.创建模板项目框架             2.创建模板文件在项目文件夹根目录创建.template.config文件夹,在文件夹下创建新的文件:template.json  ......
  • aspnetcore 原生 DI 实现基于 key 的服务获取
    你可能想通过一个字符串或者其他的类型来获取一个具体的服务实现,那么在aspnetcore原生的MSDI中,如何实现呢?本文将介绍如何通过自定义工厂来实现。我们现在恰好有基于J......
  • aspnetcore 原生 DI 实现基于 key 的服务获取
    你可能想通过一个字符串或者其他的类型来获取一个具体的服务实现,那么在aspnetcore原生的MSDI中,如何实现呢?本文将介绍如何通过自定义工厂来实现。我们现在恰好有基于J......
  • aspnetcore 原生 DI 实现基于 key 的服务获取
    你可能想通过一个字符串或者其他的类型来获取一个具体的服务实现,那么在aspnetcore原生的MSDI中,如何实现呢?本文将介绍如何通过自定义工厂来实现。我们现在恰好有基于J......