之前因为工作原因使用spring cloud全家桶开发过若干项目,发现其中的feign非常好用,以前开发接口客户端的时候都是重复使用HttpClient实现业务,每次新增接口都十分繁琐,故萌生了自定义一个feign.net的想法,直到最近辞去工作后有了时间着手开发. 关于feign的介绍就不多说了,网上有大量的资料.
个人无太多精力复制feign的全部功能,只挑一些比较重要的来实现.
首先定义一个 FeignClientAttribute,其中的Fallback和FallbackFactory主要用于服务降级.
/// <summary> /// a feign client service /// </summary> [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public class FeignClientAttribute : Attribute { public FeignClientAttribute(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } Name = name; } public virtual string Name { get; } public virtual string Url { get; set; } public virtual Type Fallback { get; set; } public virtual Type FallbackFactory { get; set; } public FeignClientLifetime? Lifetime { get; set; } }
FeignClientLifetime为服务的生命周期
/// <summary> /// Specifies the lifetime of a service /// </summary> public enum FeignClientLifetime { /// <summary> /// Specifies that a single instance of the service will be created. /// </summary> Singleton = 0, /// <summary> /// Specifies that a new instance of the service will be created for each scope. /// </summary> Scoped = 1, /// <summary> /// Specifies that a new instance of the service will be created every time it is /// </summary> Transient = 2 }
定义RequestMapping
public class RequestMappingAttribute : RequestMappingBaseAttribute { public RequestMappingAttribute() { } public RequestMappingAttribute(string value) : this(value, "GET") { } public RequestMappingAttribute(string value, string method) : base(value) { Method = method; } public string Method { get; set; } public override string GetMethod() { return Method; } }
定义PathVariableAttribute等
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public sealed class PathVariableAttribute : Attribute, IRequestParameter { public PathVariableAttribute() { } public PathVariableAttribute(string name) { Name = name; } public string Name { get; set; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public sealed class RequestBodyAttribute : Attribute, IRequestParameter { } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public sealed class RequestFormAttribute : Attribute, IRequestParameter { } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public sealed class RequestParamAttribute : Attribute, IRequestParameter { public RequestParamAttribute() { } public RequestParamAttribute(string name) { Name = name; } public string Name { get; set; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public sealed class RequestQueryAttribute : Attribute, IRequestParameter { }
为了方便扩展,定义一个管道,里面包含了服务实例初始化,发送前,发送后等的事件.
public interface IServiceFeignClientPipeline<TService> { bool Enabled { get; set; } event EventHandler<BuildingRequestEventArgs<TService>> BuildingRequest; event EventHandler<SendingRequestEventArgs<TService>> SendingRequest; event EventHandler<CancelRequestEventArgs<TService>> CancelRequest; event EventHandler<ErrorRequestEventArgs<TService>> ErrorRequest; event EventHandler<ReceivingResponseEventArgs<TService>> ReceivingResponse; event EventHandler<InitializingEventArgs<TService>> Initializing; event EventHandler<DisposingEventArgs<TService>> Disposing; event EventHandler<FallbackRequestEventArgs<TService>> FallbackRequest; } /// <summary> /// 全局Pipeline /// </summary> public interface IGlobalFeignClientPipeline : IServiceFeignClientPipeline<object> { IServiceFeignClientPipeline<object> GetServicePipeline(string serviceId); IServiceFeignClientPipeline<object> GetOrAddServicePipeline(string serviceId); IServiceFeignClientPipeline<TService> GetServicePipeline<TService>(); IServiceFeignClientPipeline<TService> GetOrAddServicePipeline<TService>(); }
基本的声明有了,然后开始实现动态生成代理类型代码.
先定义一个Proxy父类
public interface IFeignClient { /// <summary> /// Gets the serviceId /// </summary> string ServiceId { get; } } public interface IFeignClient<out TService> : IFeignClient { TService Service { get; } } public abstract partial class FeignClientHttpProxy<TService> : IFeignClient<TService>, IDisposable where TService : class { public FeignClientHttpProxy(IFeignOptions feignOptions, IServiceDiscovery serviceDiscovery, ICacheProvider cacheProvider, ILoggerFactory loggerFactory) { _feignOptions = feignOptions; _globalFeignClientPipeline = _feignOptions?.FeignClientPipeline as GlobalFeignClientPipeline; _serviceIdFeignClientPipeline = _globalFeignClientPipeline?.GetServicePipeline(ServiceId); _serviceFeignClientPipeline = _globalFeignClientPipeline?.GetServicePipeline<TService>(); _logger = loggerFactory?.CreateLogger(typeof(FeignClientHttpProxy<TService>)); ServiceDiscoveryHttpClientHandler<TService> serviceDiscoveryHttpClientHandler = new ServiceDiscoveryHttpClientHandler<TService>(this, serviceDiscovery, cacheProvider, _logger); serviceDiscoveryHttpClientHandler.ShouldResolveService = string.IsNullOrWhiteSpace(Url); serviceDiscoveryHttpClientHandler.AllowAutoRedirect = false; HttpClient = new HttpClient(serviceDiscoveryHttpClientHandler); string baseUrl = serviceDiscoveryHttpClientHandler.ShouldResolveService ? ServiceId ?? "" : Url; if (!baseUrl.StartsWith("http")) { baseUrl = $"http://{baseUrl}"; } if (!string.IsNullOrWhiteSpace(BaseUri)) { if (baseUrl.EndsWith("/")) { baseUrl = baseUrl.TrimEnd(‘/‘); } if (BaseUri.StartsWith("/")) { baseUrl += BaseUri; } else { baseUrl += "/" + BaseUri; } } if (baseUrl.EndsWith("/")) { baseUrl = baseUrl.TrimEnd(‘/‘); } BaseUrl = baseUrl; InitializingEventArgs<TService> initializingEventArgs = new InitializingEventArgs<TService>(this); initializingEventArgs.HttpClient = HttpClient; OnInitializing(initializingEventArgs); HttpClient = initializingEventArgs.HttpClient; if (HttpClient == null) { throw new ArgumentNullException(nameof(HttpClient)); } } internal GlobalFeignClientPipeline _globalFeignClientPipeline; internal ServiceIdFeignClientPipeline _serviceIdFeignClientPipeline; internal ServiceFeignClientPipeline<TService> _serviceFeignClientPipeline; ILogger _logger; IFeignOptions _feignOptions; protected IFeignOptions FeignOptions => _feignOptions; TService IFeignClient<TService>.Service { get { return this as TService; } } public abstract string ServiceId { get; } protected virtual bool IsResponseTerminatedRequest => true; public virtual string BaseUri { get { return null; } } public virtual string Url { get { return null; } } protected string BaseUrl { get; } protected HttpClient HttpClient { get; } #region IDisposable Support private bool disposedValue = false; // 要检测冗余调用 protected virtual void Dispose(bool disposing) { if (!disposedValue) { DisposingEventArgs<TService> disposingEventArgs = new DisposingEventArgs<TService>(this, disposing); OnDisposing(disposingEventArgs); if (disposing) { // TODO: 释放托管状态(托管对象)。 } // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。 // TODO: 将大型字段设置为 null。 HttpClient.Dispose(); disposedValue = true; } } // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。 //~FeignClientServiceBase() //{ // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 // Dispose(false); //} // 添加此代码以正确实现可处置模式。 void IDisposable.Dispose() { // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。 //GC.SuppressFinalize(this); } #endregion #region PathVariable protected string ReplacePathVariable<T>(string uri, string name, T value) { return FeignClientUtils.ReplacePathVariable<T>(_feignOptions.Converters, uri, name, value); } #endregion #region RequestParam protected string ReplaceRequestParam<T>(string uri, string name, T value) { return FeignClientUtils.ReplaceRequestParam<T>(_feignOptions.Converters, uri, name, value); } #endregion #region RequestQuery protected string ReplaceRequestQuery<T>(string uri, string name, T value) { return FeignClientUtils.ReplaceRequestQuery<T>(_feignOptions.Converters, uri, name, value); } #endregion }
public class FeignProxyHttpClientHandler<TService> : HttpClientHandler where TService : class { private readonly ILogger _logger; private FeignClientHttpProxy<TService> _feignClient; //IFeignClient IFeignHttpClientHandler.FeignClient => _feignClient; /// <summary> /// Initializes a new instance of the <see cref="FeignHttpClientHandler"/> class. /// </summary> public FeignProxyHttpClientHandler(FeignClientHttpProxy<TService> feignClient, ILogger logger) { _feignClient = feignClient; _logger = logger; } protected virtual Uri LookupRequestUri(Uri uri) { return uri; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { FeignHttpRequestMessage feignRequest = request as FeignHttpRequestMessage; return SendInternalAsync(feignRequest, cancellationToken); } async Task<HttpResponseMessage> SendInternalAsync(FeignHttpRequestMessage request, CancellationToken cancellationToken) { var current = request.RequestUri; try { #region BuildingRequest BuildingRequestEventArgs<TService> buildingArgs = new BuildingRequestEventArgs<TService>(_feignClient, request.Method.ToString(), request.RequestUri, new Dictionary<string, string>()); _feignClient.OnBuildingRequest(buildingArgs); //request.Method = new HttpMethod(buildingArgs.Method); request.RequestUri = buildingArgs.RequestUri; if (buildingArgs.Headers != null && buildingArgs.Headers.Count > 0) { foreach (var item in buildingArgs.Headers) { request.Headers.TryAddWithoutValidation(item.Key, item.Value); } } #endregion request.RequestUri = LookupRequestUri(request.RequestUri); #region SendingRequest SendingRequestEventArgs<TService> sendingArgs = new SendingRequestEventArgs<TService>(_feignClient, request); _feignClient.OnSendingRequest(sendingArgs); if (sendingArgs.IsTerminated) { //请求被终止 throw new TerminatedRequestException(); } request = sendingArgs.RequestMessage; if (request == null) { _logger?.LogError($"SendingRequest is null;"); return new HttpResponseMessage(System.Net.HttpStatusCode.ExpectationFailed) { Content = new StringContent(""), //Headers = new System.Net.Http.Headers.HttpResponseHeaders(), RequestMessage = request }; } #endregion #region CannelRequest CancelRequestEventArgs<TService> cancelArgs = new CancelRequestEventArgs<TService>(_feignClient, cancellationToken); _feignClient.OnCancelRequest(cancelArgs); #endregion return await base.SendAsync(request, cancellationToken); } catch (Exception e) { if (!e.IsSkipLog()) { _logger?.LogError(e, "Exception during SendAsync()"); } if (e is HttpRequestException) { FeignHttpRequestException feignHttpRequestException = new FeignHttpRequestException(_feignClient, request, (HttpRequestException)e); ExceptionDispatchInfo exceptionDispatchInfo = ExceptionDispatchInfo.Capture(feignHttpRequestException); exceptionDispatchInfo.Throw(); } throw; } finally { request.RequestUri = current; } } } public class ServiceDiscoveryHttpClientHandler<TService> : FeignProxyHttpClientHandler<TService> where TService : class { private IServiceResolve _serviceResolve; private IServiceDiscovery _serviceDiscovery; private ICacheProvider _serviceCacheProvider; /// <summary> /// Initializes a new instance of the <see cref="ServiceDiscoveryHttpClientHandler"/> class. /// </summary> public ServiceDiscoveryHttpClientHandler(FeignClientHttpProxy<TService> feignClient, IServiceDiscovery serviceDiscovery, ICacheProvider serviceCacheProvider, ILogger logger) : base(feignClient, logger) { _serviceResolve = new RandomServiceResolve(logger); _serviceDiscovery = serviceDiscovery; _serviceCacheProvider = serviceCacheProvider; ShouldResolveService = true; } public bool ShouldResolveService { get; set; } protected override Uri LookupRequestUri(Uri uri) { if (!ShouldResolveService) { return uri; } if (_serviceDiscovery == null) { return uri; } IList<IServiceInstance> services = _serviceDiscovery.GetServiceInstancesWithCache(uri.Host, _serviceCacheProvider); return _serviceResolve.ResolveService(uri, services); } }
接下来开始生成代理类
TypeBuilder
private TypeBuilder CreateTypeBuilder(string typeName, Type parentType) { return _dynamicAssembly.ModuleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, parentType); }
构造函数
void BuildConstructor(TypeBuilder typeBuilder, Type parentType) { ConstructorInfo baseConstructorInfo = GetConstructor(parentType); var parameterTypes = baseConstructorInfo.GetParameters().Select(s => s.ParameterType).ToArray(); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, parameterTypes); ILGenerator constructorIlGenerator = constructorBuilder.GetILGenerator(); constructorIlGenerator.Emit(OpCodes.Ldarg_0); for (int i = 1; i <= baseConstructorInfo.GetParameters().Length; i++) { constructorIlGenerator.Emit(OpCodes.Ldarg_S, i); } constructorIlGenerator.Emit(OpCodes.Call, baseConstructorInfo); constructorIlGenerator.Emit(OpCodes.Ret); }
重写父类只读属性
void BuildReadOnlyProperty(TypeBuilder typeBuilder, Type interfaceType, string propertyName, string propertyValue) { PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, typeof(string), Type.EmptyTypes); MethodBuilder propertyGet = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(string), Type.EmptyTypes); ILGenerator iLGenerator = propertyGet.GetILGenerator(); if (propertyValue == null) { iLGenerator.Emit(OpCodes.Ldnull); } else { iLGenerator.Emit(OpCodes.Ldstr, propertyValue); } iLGenerator.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(propertyGet); }
//serviceId BuildReadOnlyProperty(typeBuilder, serviceType, "ServiceId", serviceType.GetCustomAttribute<FeignClientAttribute>().Name); //baseUri BuildReadOnlyProperty(typeBuilder, serviceType, "BaseUri", serviceType.GetCustomAttribute<RequestMappingAttribute>()?.Value); // url if (serviceType.GetCustomAttribute<FeignClientAttribute>().Url != null) { BuildReadOnlyProperty(typeBuilder, serviceType, "Url", serviceType.GetCustomAttribute<FeignClientAttribute>().Url); } typeBuilder.AddInterfaceImplementation(serviceType);
接下来是最重要的,读取声明服务的所有方法,全部生成到代理类中
foreach (var method in serviceType.GetMethods()) { methodBuilder.BuildMethod(typeBuilder, serviceType, method, feignClientAttribute); }
class FeignClientHttpProxyEmitMethodBuilder : IMethodBuilder { #region define //protected static readonly MethodInfo ReplacePathVariableMethod = typeof(FeignClientHttpProxy<>).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o => o.IsGenericMethod && o.Name == "ReplacePathVariable"); //protected static readonly MethodInfo ReplaceRequestParamMethod = typeof(FeignClientHttpProxy<>).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o => o.IsGenericMethod && o.Name == "ReplaceRequestParam"); //protected static readonly MethodInfo ReplaceRequestQueryMethod = typeof(FeignClientHttpProxy<>).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o => o.IsGenericMethod && o.Name == "ReplaceRequestQuery"); protected static MethodInfo GetReplacePathVariableMethod(TypeBuilder typeBuilder) { return typeBuilder.BaseType.GetMethod("ReplacePathVariable", BindingFlags.Instance | BindingFlags.NonPublic); } protected static MethodInfo GetReplaceRequestParamMethod(TypeBuilder typeBuilder) { return typeBuilder.BaseType.GetMethod("ReplaceRequestParam", BindingFlags.Instance | BindingFlags.NonPublic); } protected static MethodInfo GetReplaceRequestQueryMethod(TypeBuilder typeBuilder) { return typeBuilder.BaseType.GetMethod("ReplaceRequestQuery", BindingFlags.Instance | BindingFlags.NonPublic); } #endregion public void BuildMethod(TypeBuilder typeBuilder, Type serviceType, MethodInfo method, FeignClientAttribute feignClientAttribute) { BuildMethod(typeBuilder, serviceType, method, feignClientAttribute, GetRequestMappingAttribute(method)); } void BuildMethod(TypeBuilder typeBuilder, Type serviceType, MethodInfo method, FeignClientAttribute feignClientAttribute, RequestMappingBaseAttribute requestMapping) { MethodBuilder methodBuilder = CreateMethodBuilder(typeBuilder, method); ILGenerator iLGenerator = methodBuilder.GetILGenerator(); if (requestMapping == null) { iLGenerator.Emit(OpCodes.Newobj, typeof(NotSupportedException).GetConstructor(Type.EmptyTypes)); iLGenerator.Emit(OpCodes.Throw); return; } string uri = requestMapping.Value ?? ""; LocalBuilder local_Uri = iLGenerator.DeclareLocal(typeof(string)); // uri LocalBuilder local_OldValue = iLGenerator.DeclareLocal(typeof(string)); // temp iLGenerator.Emit(OpCodes.Ldstr, uri); iLGenerator.Emit(OpCodes.Stloc, local_Uri); List<EmitRequestContent> emitRequestContents = EmitParameter(typeBuilder, iLGenerator, method, local_Uri, local_OldValue); EmitCallMethod(typeBuilder, methodBuilder, iLGenerator, serviceType, method, requestMapping, local_Uri, emitRequestContents); } protected MethodInfo GetInvokeMethod(Type serviceType, MethodInfo method, RequestMappingBaseAttribute requestMapping) { if (method.IsTaskMethod()) { if (method.ReturnType.IsGenericType) { return GetInvokeMethod(serviceType, requestMapping, method.ReturnType.GenericTypeArguments[0], true); } return GetInvokeMethod(serviceType, requestMapping, method.ReturnType, true); } return GetInvokeMethod(serviceType, requestMapping, method.ReturnType, false); } protected virtual MethodInfo GetInvokeMethod(Type serviceType, RequestMappingBaseAttribute requestMapping, Type returnType, bool async) { MethodInfo httpClientMethod; bool isGeneric = !(returnType == null || returnType == typeof(void) || returnType == typeof(Task)); if (isGeneric) { //httpClientMethod = async ? FeignClientHttpProxy<object>.HTTP_SEND_ASYNC_GENERIC_METHOD : FeignClientHttpProxy<object>.HTTP_SEND_GENERIC_METHOD; httpClientMethod = async ? FeignClientHttpProxy<object>.GetHttpSendAsyncGenericMethod(serviceType) : FeignClientHttpProxy<object>.GetHttpSendGenericMethod(serviceType); } else { // httpClientMethod = async ? FeignClientHttpProxy<object>.HTTP_SEND_ASYNC_METHOD : FeignClientHttpProxy<object>.HTTP_SEND_METHOD; httpClientMethod = async ? FeignClientHttpProxy<object>.GetHttpSendAsyncMethod(serviceType) : FeignClientHttpProxy<object>.GetHttpSendMethod(serviceType); } if (isGeneric) { return httpClientMethod.MakeGenericMethod(returnType); } return httpClientMethod; } protected bool SupportRequestContent(MethodInfo method, RequestMappingBaseAttribute requestMappingBaseAttribute) { return "POST".Equals(requestMappingBaseAttribute.GetMethod(), StringComparison.OrdinalIgnoreCase) || "PUT".Equals(requestMappingBaseAttribute.GetMethod(), StringComparison.OrdinalIgnoreCase); } protected RequestMappingBaseAttribute GetRequestMappingAttribute(MethodInfo method) { if (method.IsDefined(typeof(RequestMappingBaseAttribute))) { RequestMappingBaseAttribute[] requestMappingBaseAttributes = method.GetCustomAttributes<RequestMappingBaseAttribute>().ToArray(); if (requestMappingBaseAttributes.Length > 1) { throw new ArgumentException(nameof(requestMappingBaseAttributes.Length)); } return requestMappingBaseAttributes[0]; } string methodName = method.Name.ToLower(); if (methodName.StartsWith("get") || methodName.StartsWith("query") || methodName.StartsWith("select")) { //get return new GetMappingAttribute(); } else if (methodName.StartsWith("post") || methodName.StartsWith("create") || methodName.StartsWith("insert")) { //post return new PostMappingAttribute(); } else if (methodName.StartsWith("put") || methodName.StartsWith("update")) { //put return new PutMappingAttribute(); } else if (methodName.StartsWith("delete") || methodName.StartsWith("remove")) { //delete return new DeleteMappingAttribute(); } return null; } protected MethodBuilder CreateMethodBuilder(TypeBuilder typeBuilder, MethodInfo method) { MethodAttributes methodAttributes; if (method.IsVirtual) { //methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual; methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final; } else { methodAttributes = MethodAttributes.Public; } var arguments = method.GetParameters().Select(a => a.ParameterType).ToArray(); MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name, methodAttributes, CallingConventions.Standard, method.ReturnType, arguments); typeBuilder.DefineMethodOverride(methodBuilder, method); return methodBuilder; } protected virtual void EmitCallMethod(TypeBuilder typeBuilder, MethodBuilder methodBuilder, ILGenerator iLGenerator, Type serviceType, MethodInfo method, RequestMappingBaseAttribute requestMapping, LocalBuilder uri, List<EmitRequestContent> emitRequestContents) { var invokeMethod = GetInvokeMethod(serviceType, method, requestMapping); if (emitRequestContents != null && emitRequestContents.Count > 0 && !SupportRequestContent(invokeMethod, requestMapping)) { throw new NotSupportedException("不支持RequestBody或者RequestForm"); } LocalBuilder feignClientRequest = DefineFeignClientRequest(typeBuilder, serviceType, iLGenerator, uri, requestMapping, emitRequestContents, method); iLGenerator.Emit(OpCodes.Ldarg_0); //this iLGenerator.Emit(OpCodes.Ldloc, feignClientRequest); iLGenerator.Emit(OpCodes.Call, invokeMethod); iLGenerator.Emit(OpCodes.Ret); } protected LocalBuilder DefineFeignClientRequest(TypeBuilder typeBuilder, Type serviceType, ILGenerator iLGenerator, LocalBuilder uri, RequestMappingBaseAttribute requestMapping, List<EmitRequestContent> emitRequestContents, MethodInfo methodInfo) { LocalBuilder localBuilder = iLGenerator.DeclareLocal(typeof(FeignClientHttpRequest)); // baseUrl EmitBaseUrl(iLGenerator, serviceType); //mapping uri if (requestMapping.Value == null) { iLGenerator.Emit(OpCodes.Ldnull); } else { iLGenerator.Emit(OpCodes.Ldstr, requestMapping.Value); } //uri iLGenerator.Emit(OpCodes.Ldloc, uri); //httpMethod iLGenerator.Emit(OpCodes.Ldstr, requestMapping.GetMethod()); //contentType string contentType = requestMapping.ContentType; if (string.IsNullOrWhiteSpace(contentType) && serviceType.IsDefined(typeof(RequestMappingAttribute))) { contentType = serviceType.GetCustomAttribute<RequestMappingAttribute>().ContentType; } if (contentType == null) { iLGenerator.Emit(OpCodes.Ldnull); } else { iLGenerator.Emit(OpCodes.Ldstr, contentType); } //requestContent if (emitRequestContents != null && emitRequestContents.Count > 0) { if (emitRequestContents.Count == 1) { if (typeof(IHttpRequestFile).IsAssignableFrom(emitRequestContents[0].Parameter.ParameterType)) { EmitFeignClientMultipartRequestContent(iLGenerator, emitRequestContents); } else { EmitFeignClientRequestContent(iLGenerator, emitRequestContents[0], null); } } else if (emitRequestContents.Any(s => !s.SupportMultipart)) { throw new NotSupportedException("最多只支持一个RequestContent"); } else { EmitFeignClientMultipartRequestContent(iLGenerator, emitRequestContents); } } else { iLGenerator.Emit(OpCodes.Ldnull); } //method // method=null LocalBuilder methodInfoLocalBuilder = iLGenerator.DeclareLocal(typeof(MethodInfo)); iLGenerator.Emit(OpCodes.Ldnull); iLGenerator.Emit(OpCodes.Stloc, methodInfoLocalBuilder); Label newFeingClientRequestLabel = iLGenerator.DefineLabel(); #region if (base.FeignOptions.IncludeMethodMetadata) set the call method PropertyInfo feignOptionsProperty = typeBuilder.BaseType.GetProperty("FeignOptions", BindingFlags.Instance | BindingFlags.NonPublic); PropertyInfo includeMethodMetadataProperty = feignOptionsProperty.PropertyType.GetProperty("IncludeMethodMetadata"); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Call, feignOptionsProperty.GetMethod); iLGenerator.Emit(OpCodes.Call, includeMethodMetadataProperty.GetMethod); iLGenerator.Emit(OpCodes.Ldc_I4, 1); iLGenerator.Emit(OpCodes.Ceq); iLGenerator.Emit(OpCodes.Brfalse_S, newFeingClientRequestLabel); ReflectionHelper.EmitMethodInfo(iLGenerator, methodInfo); iLGenerator.Emit(OpCodes.Stloc, methodInfoLocalBuilder); #endregion iLGenerator.MarkLabel(newFeingClientRequestLabel); iLGenerator.Emit(OpCodes.Ldloc, methodInfoLocalBuilder); iLGenerator.Emit(OpCodes.Newobj, typeof(FeignClientHttpRequest).GetConstructors()[0]); iLGenerator.Emit(OpCodes.Stloc, localBuilder); return localBuilder; } void EmitFeignClientRequestContent(ILGenerator iLGenerator, EmitRequestContent emitRequestContent, LocalBuilder localBuilder) { if (typeof(IHttpRequestFileForm).IsAssignableFrom(emitRequestContent.Parameter.ParameterType)) { //iLGenerator.Emit(OpCodes.Ldstr, emitRequestContent.Parameter.Name); iLGenerator.Emit(OpCodes.Ldarg_S, emitRequestContent.ParameterIndex); iLGenerator.Emit(OpCodes.Newobj, typeof(FeignClientHttpFileFormRequestContent).GetConstructors()[0]); if (localBuilder != null) { iLGenerator.Emit(OpCodes.Stloc, localBuilder); } return; } if (typeof(IHttpRequestFile).IsAssignableFrom(emitRequestContent.Parameter.ParameterType)) { iLGenerator.Emit(OpCodes.Ldstr, emitRequestContent.Parameter.Name); iLGenerator.Emit(OpCodes.Ldarg_S, emitRequestContent.ParameterIndex); iLGenerator.Emit(OpCodes.Newobj, typeof(FeignClientHttpFileRequestContent).GetConstructors()[0]); if (localBuilder != null) { iLGenerator.Emit(OpCodes.Stloc, localBuilder); } return; } ConstructorInfo constructorInfo; switch (emitRequestContent.MediaType) { case "application/json": constructorInfo = typeof(FeignClientHttpJsonRequestContent<>).MakeGenericType(emitRequestContent.Parameter.ParameterType).GetConstructors()[0]; break; case "application/x-www-form-urlencoded": constructorInfo = typeof(FeignClientHttpFormRequestContent<>).MakeGenericType(emitRequestContent.Parameter.ParameterType).GetConstructors()[0]; break; default: throw new NotSupportedException("不支持的content type"); //constructorInfo = typeof(FeignClientFormRequestContent<>).MakeGenericType(emitRequestContent.Parameter.ParameterType).GetConstructors()[0]; //break; }; iLGenerator.Emit(OpCodes.Ldstr, emitRequestContent.Parameter.Name); iLGenerator.Emit(OpCodes.Ldarg_S, emitRequestContent.ParameterIndex); iLGenerator.Emit(OpCodes.Newobj, constructorInfo); if (localBuilder != null) { iLGenerator.Emit(OpCodes.Stloc, localBuilder); } } void EmitFeignClientMultipartRequestContent(ILGenerator iLGenerator, List<EmitRequestContent> emitRequestContents) { LocalBuilder requestContent = iLGenerator.DeclareLocal(typeof(FeignClientHttpMultipartFormRequestContent)); MethodInfo methodAddContent = typeof(FeignClientHttpMultipartFormRequestContent).GetMethod("AddContent"); iLGenerator.Emit(OpCodes.Newobj, typeof(FeignClientHttpMultipartFormRequestContent).GetConstructors()[0]); iLGenerator.Emit(OpCodes.Stloc, requestContent); for (int i = 0; i < emitRequestContents.Count; i++) { LocalBuilder childRequestContent = iLGenerator.DeclareLocal(typeof(FeignClientHttpRequestContent)); EmitFeignClientRequestContent(iLGenerator, emitRequestContents[i], childRequestContent); iLGenerator.Emit(OpCodes.Ldloc, requestContent); iLGenerator.Emit(OpCodes.Ldstr, emitRequestContents[i].Parameter.Name); iLGenerator.Emit(OpCodes.Ldloc, childRequestContent); iLGenerator.Emit(OpCodes.Call, methodAddContent); } iLGenerator.Emit(OpCodes.Ldloc, requestContent); } void EmitBaseUrl(ILGenerator iLGenerator, Type serviceType) { PropertyInfo propertyInfo = typeof(FeignClientHttpProxy<>).MakeGenericType(serviceType).GetProperty("BaseUrl", BindingFlags.Instance | BindingFlags.NonPublic); iLGenerator.Emit(OpCodes.Ldarg_0); //this iLGenerator.Emit(OpCodes.Callvirt, propertyInfo.GetMethod); } protected List<EmitRequestContent> EmitParameter(TypeBuilder typeBuilder, ILGenerator iLGenerator, MethodInfo method, LocalBuilder uri, LocalBuilder value) { int index = 0; List<EmitRequestContent> emitRequestContents = new List<EmitRequestContent>(); foreach (var parameterInfo in method.GetParameters()) { index++; if (typeof(IHttpRequestFileForm).IsAssignableFrom(parameterInfo.ParameterType)) { emitRequestContents.Add(new EmitRequestContent { MediaType = Constants.MediaTypes.MULTIPART_FORMDATA, Parameter = parameterInfo, SupportMultipart = false, ParameterIndex = index }); continue; } if (typeof(IHttpRequestFile).IsAssignableFrom(parameterInfo.ParameterType)) { emitRequestContents.Add(new EmitRequestContent { MediaType = Constants.MediaTypes.FORMDATA, Parameter = parameterInfo, SupportMultipart = true, ParameterIndex = index }); continue; } if (parameterInfo.IsDefined(typeof(RequestBodyAttribute))) { emitRequestContents.Add(new EmitRequestContent { MediaType = Constants.MediaTypes.APPLICATION_JSON, Parameter = parameterInfo, SupportMultipart = false, ParameterIndex = index }); continue; } if (parameterInfo.IsDefined(typeof(RequestFormAttribute))) { emitRequestContents.Add(new EmitRequestContent { MediaType = Constants.MediaTypes.APPLICATION_FORM_URLENCODED, Parameter = parameterInfo, SupportMultipart = true, ParameterIndex = index }); continue; } MethodInfo replaceValueMethod; string name; if (parameterInfo.IsDefined(typeof(RequestParamAttribute))) { name = parameterInfo.GetCustomAttribute<RequestParamAttribute>().Name ?? parameterInfo.Name; //replaceValueMethod = ReplaceRequestParamMethod; replaceValueMethod = GetReplaceRequestParamMethod(typeBuilder); } else if (parameterInfo.IsDefined(typeof(RequestQueryAttribute))) { name = parameterInfo.Name; //replaceValueMethod = ReplaceRequestQueryMethod; replaceValueMethod = GetReplaceRequestQueryMethod(typeBuilder); } else { name = parameterInfo.IsDefined(typeof(PathVariableAttribute)) ? parameterInfo.GetCustomAttribute<PathVariableAttribute>().Name : parameterInfo.Name; //replaceValueMethod = ReplacePathVariableMethod; replaceValueMethod = GetReplacePathVariableMethod(typeBuilder); } if (string.IsNullOrWhiteSpace(name)) { name = parameterInfo.Name; } iLGenerator.Emit(OpCodes.Ldstr, name); iLGenerator.Emit(OpCodes.Stloc, value); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldloc, uri); iLGenerator.Emit(OpCodes.Ldloc, value); iLGenerator.Emit(OpCodes.Ldarg_S, index); replaceValueMethod = replaceValueMethod.MakeGenericMethod(parameterInfo.ParameterType); iLGenerator.Emit(OpCodes.Call, replaceValueMethod); iLGenerator.Emit(OpCodes.Stloc, uri); } return emitRequestContents; } } class FallbackFeignClientHttpProxyEmitMethodBuilder : FeignClientHttpProxyEmitMethodBuilder { public FallbackFeignClientHttpProxyEmitMethodBuilder(DynamicAssembly dynamicAssembly) { _dynamicAssembly = dynamicAssembly; } DynamicAssembly _dynamicAssembly; protected override MethodInfo GetInvokeMethod(Type serviceType, RequestMappingBaseAttribute requestMapping, Type returnType, bool async) { MethodInfo httpClientMethod; bool isGeneric = !(returnType == null || returnType == typeof(void) || returnType == typeof(Task)); if (isGeneric) { //httpClientMethod = async ? FallbackFeignClientHttpProxy<object, object>.HTTP_SEND_ASYNC_GENERIC_METHOD_FALLBACK : FallbackFeignClientHttpProxy<object, object>.HTTP_SEND_GENERIC_METHOD_FALLBACK; httpClientMethod = async ? FallbackFeignClientHttpProxy<object, object>.GetHttpSendAsyncGenericFallbackMethod(serviceType, serviceType) : FallbackFeignClientHttpProxy<object, object>.GetHttpSendGenericFallbackMethod(serviceType, serviceType); } else { //httpClientMethod = async ? FallbackFeignClientHttpProxy<object, object>.HTTP_SEND_ASYNC_METHOD_FALLBACK : FallbackFeignClientHttpProxy<object, object>.HTTP_SEND_METHOD_FALLBACK; httpClientMethod = async ? FallbackFeignClientHttpProxy<object, object>.GetHttpSendAsyncFallbackMethod(serviceType, serviceType) : FallbackFeignClientHttpProxy<object, object>.GetHttpSendFallbackMethod(serviceType, serviceType); } if (isGeneric) { return httpClientMethod.MakeGenericMethod(returnType); } return httpClientMethod; } protected override void EmitCallMethod(TypeBuilder typeBuilder, MethodBuilder methodBuilder, ILGenerator iLGenerator, Type serviceType, MethodInfo method, RequestMappingBaseAttribute requestMapping, LocalBuilder uri, List<EmitRequestContent> emitRequestContents) { var invokeMethod = GetInvokeMethod(serviceType, method, requestMapping); if (emitRequestContents != null && emitRequestContents.Count > 0 && !SupportRequestContent(invokeMethod, requestMapping)) { throw new NotSupportedException("不支持RequestBody或者RequestForm"); } LocalBuilder feignClientRequest = DefineFeignClientRequest(typeBuilder, serviceType, iLGenerator, uri, requestMapping, emitRequestContents, method); // fallback LocalBuilder fallbackDelegate = DefineFallbackDelegate(typeBuilder, methodBuilder, iLGenerator, serviceType, method); iLGenerator.Emit(OpCodes.Ldarg_0); //this iLGenerator.Emit(OpCodes.Ldloc, feignClientRequest); iLGenerator.Emit(OpCodes.Ldloc, fallbackDelegate); iLGenerator.Emit(OpCodes.Call, invokeMethod); iLGenerator.Emit(OpCodes.Ret); } LocalBuilder DefineFallbackDelegate(TypeBuilder typeBuilder, MethodBuilder methodBuilder, ILGenerator iLGenerator, Type serviceType, MethodInfo method) { Type delegateType; if (method.ReturnType == null || method.ReturnType == typeof(void)) { delegateType = typeof(Action); } else { delegateType = typeof(Func<>).MakeGenericType(method.ReturnType); } int bindingFlagsValue = 0; foreach (BindingFlags item in Enum.GetValues(typeof(BindingFlags))) { bindingFlagsValue += item.GetHashCode(); } var delegateConstructor = delegateType.GetConstructors((BindingFlags)bindingFlagsValue)[0]; LocalBuilder invokeDelegate = iLGenerator.DeclareLocal(delegateType); // if has parameters if (method.GetParameters().Length > 0) { var anonymousMethodClassTypeBuild = FallbackProxyAnonymousMethodClassBuilder.BuildType(_dynamicAssembly.ModuleBuilder, serviceType, method); // new anonymousMethodClass LocalBuilder anonymousMethodClass = iLGenerator.DeclareLocal(anonymousMethodClassTypeBuild.Item1); //field iLGenerator.Emit(OpCodes.Ldarg_0); //this iLGenerator.Emit(OpCodes.Call, typeBuilder.BaseType.GetProperty("Fallback").GetMethod); //.Fallback for (int i = 1; i <= method.GetParameters().Length; i++) { iLGenerator.Emit(OpCodes.Ldarg_S, i); } iLGenerator.Emit(OpCodes.Newobj, anonymousMethodClassTypeBuild.Item2); iLGenerator.Emit(OpCodes.Stloc, anonymousMethodClass); iLGenerator.Emit(OpCodes.Ldloc, anonymousMethodClass); iLGenerator.Emit(OpCodes.Ldftn, anonymousMethodClassTypeBuild.Item3); } else { iLGenerator.Emit(OpCodes.Ldarg_0); //this iLGenerator.Emit(OpCodes.Call, typeBuilder.BaseType.GetProperty("Fallback").GetMethod); //.Fallback iLGenerator.Emit(OpCodes.Ldftn, method); } iLGenerator.Emit(OpCodes.Newobj, delegateConstructor); iLGenerator.Emit(OpCodes.Stloc, invokeDelegate); return invokeDelegate; } }
最后看看效果
声明的服务
[CustomFeignClient("yun-platform-service-provider" , Fallback = typeof(TestServiceFallback) //, FallbackFactory = typeof(TestServiceFallbackFactory) //, Url = "http://localhost:8802/" //, Url = "http://10.1.5.90:8802/" //, Url = "http://localhost:62088/" )] [RequestMapping("/organizations")] public interface ITestService { //string Name { get; } [RequestMapping("/{id}/asdasdsad", Method = "POST")] Task PostValueAsync(); [RequestMapping("/Values/uploadFile", Method = "POST")] Task<string> UploadFileAsync(IHttpRequestFile file, [RequestForm] TestServiceParam param); [RequestMapping("/Values/uploadFile", Method = "POST")] Task<string> UploadFileAsync(IHttpRequestFile file, [RequestForm] string name); [RequestMapping("/Values/uploadFile", Method = "POST")] Task<string> UploadFileAsync(TestServiceUploadFileParam param); [RequestMapping("/Values/formTest", Method = "POST")] Task<string> FormTestAsync([RequestForm] TestServiceParam param); [RequestMapping("/Values/uploadFiles", Method = "POST")] Task<string> UploadFilesAsync(IHttpRequestFile file1, IHttpRequestFile file2, IHttpRequestFile file3); [RequestMapping("/{id}", Method = "GET")] Task<QueryResult<JObject>> GetQueryResultValueAsync([PathVariable("id")]string id, [RequestQuery] TestServiceParam param); [RequestMapping("/{id}", Method = "GET")] QueryResult<JObject> GetQueryResultValue([PathVariable("id")]string id, [RequestQuery] TestServiceParam param); //[RequestMapping("/{id}", Method = "GET")] //Task<JObject> GetValueAsync([PathVariable("id")]string id); //[RequestMapping("/{id}", Method = "GET")] //Task<JObject> GetValueAsync([PathVariable]int id, [RequestParam] string test); //[GetMapping("/{id}")] //Task<JObject> GetValueAsync([PathVariable]int id, [RequestQuery] TestServiceParam param); [RequestMapping("/{id}")] void GetValueVoid([PathVariable]int id, [RequestParam] string test, [RequestQuery] TestServiceParam param); [RequestMapping("/{id}")] Task GetValueVoidAsync([PathVariable]int id, [RequestParam] string test, [RequestQuery] TestServiceParam param); [RequestMapping("/{id}", Method = "POST")] Task PostValueAsync([PathVariable]int id, [RequestParam] string test, [RequestBody] TestServiceParam param); [RequestMapping("/{id}", Method = "POST")] Task PostValueFormAsync([PathVariable]int id, [RequestParam] string test, [RequestForm] TestServiceParam param); [RequestMapping("/{id}", Method = "POST")] Task PostValueForm2Async([PathVariable]int id, [RequestParam] string test, [RequestForm] TestServiceParam param1, [RequestForm] TestServiceParam param2); [RequestMapping("/{id}")] void GetValueVoid([PathVariable]int id, [RequestParam] TestServiceParam queryParam, [RequestQuery] TestServiceParam param); //[GetMapping("/{id}")] //Task<JObject> GetValueAsync([PathVariable]int id, [RequestParam] string test, [RequestQuery] TestServiceParam param); }
生成的dll代码
// Token: 0x02000002 RID: 2 [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)] public class ITestService_Proxy_115A31A563E54DEF888C90E5FA6CAC78 : FallbackFeignClientHttpProxy<ITestService, TestServiceFallback>, ITestService { // Token: 0x06000001 RID: 1 RVA: 0x00002E0C File Offset: 0x0000100C public ITestService_Proxy_115A31A563E54DEF888C90E5FA6CAC78(IFeignOptions A_1, IServiceDiscovery A_2, ICacheProvider A_3, ILoggerFactory A_4, TestServiceFallback A_5) : base(A_1, A_2, A_3, A_4, A_5) { } // Token: 0x17000001 RID: 1 // (get) Token: 0x06000002 RID: 2 RVA: 0x00002E38 File Offset: 0x00001038 public override string ServiceId { get { return "yun-platform-service-provider"; } } // Token: 0x17000002 RID: 2 // (get) Token: 0x06000003 RID: 3 RVA: 0x00002E4C File Offset: 0x0000104C public override string BaseUri { get { return "/organizations"; } } // Token: 0x17000003 RID: 3 // (get) Token: 0x06000004 RID: 4 RVA: 0x00002E60 File Offset: 0x00001060 public override string Url { get { return "http://10.1.5.90:8802/"; } } // Token: 0x06000005 RID: 5 RVA: 0x00002E74 File Offset: 0x00001074 public Task PostValueAsync() { string text = "/{id}/asdasdsad"; string baseUrl = this.BaseUrl; string mappingUri = "/{id}/asdasdsad"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.PostValueAsync()); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); Func<Task> fallback = new Func<Task>(base.Fallback.PostValueAsync); return base.SendAsync(request, fallback); } // Token: 0x06000006 RID: 6 RVA: 0x00002EE4 File Offset: 0x000010E4 public Task<string> UploadFileAsync(IHttpRequestFile A_1, TestServiceParam A_2) { string text = "/Values/uploadFile"; string baseUrl = this.BaseUrl; string mappingUri = "/Values/uploadFile"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpMultipartFormRequestContent feignClientHttpMultipartFormRequestContent = new FeignClientHttpMultipartFormRequestContent(); FeignClientHttpRequestContent content = new FeignClientHttpFileRequestContent("file", A_1); feignClientHttpMultipartFormRequestContent.AddContent("file", content); FeignClientHttpRequestContent content2 = new FeignClientHttpFormRequestContent<TestServiceParam>("param", A_2); feignClientHttpMultipartFormRequestContent.AddContent("param", content2); FeignClientHttpRequestContent requestContent = feignClientHttpMultipartFormRequestContent; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.UploadFileAsync(IHttpRequestFile, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_F32799B3B1C14C489846B440A4A6DCD4 @object = new ITestService_F32799B3B1C14C489846B440A4A6DCD4(base.Fallback, A_1, A_2); Func<Task<string>> fallback = new Func<Task<string>>(@object.UploadFileAsync); return base.SendAsync<string>(request, fallback); } // Token: 0x06000007 RID: 7 RVA: 0x00002FAC File Offset: 0x000011AC public Task<string> UploadFileAsync(IHttpRequestFile A_1, string A_2) { string text = "/Values/uploadFile"; string baseUrl = this.BaseUrl; string mappingUri = "/Values/uploadFile"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpMultipartFormRequestContent feignClientHttpMultipartFormRequestContent = new FeignClientHttpMultipartFormRequestContent(); FeignClientHttpRequestContent content = new FeignClientHttpFileRequestContent("file", A_1); feignClientHttpMultipartFormRequestContent.AddContent("file", content); FeignClientHttpRequestContent content2 = new FeignClientHttpFormRequestContent<string>("name", A_2); feignClientHttpMultipartFormRequestContent.AddContent("name", content2); FeignClientHttpRequestContent requestContent = feignClientHttpMultipartFormRequestContent; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.UploadFileAsync(IHttpRequestFile, string)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_A8C583A16C3949079BB9E5BCB6209ACE @object = new ITestService_A8C583A16C3949079BB9E5BCB6209ACE(base.Fallback, A_1, A_2); Func<Task<string>> fallback = new Func<Task<string>>(@object.UploadFileAsync); return base.SendAsync<string>(request, fallback); } // Token: 0x06000008 RID: 8 RVA: 0x00003074 File Offset: 0x00001274 public Task<string> UploadFileAsync(TestServiceUploadFileParam A_1) { string text = "/Values/uploadFile"; string baseUrl = this.BaseUrl; string mappingUri = "/Values/uploadFile"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpRequestContent requestContent = new FeignClientHttpFileFormRequestContent(A_1); MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.UploadFileAsync(TestServiceUploadFileParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_20E3415B035B4A89B66A6D4BD923F0A2 @object = new ITestService_20E3415B035B4A89B66A6D4BD923F0A2(base.Fallback, A_1); Func<Task<string>> fallback = new Func<Task<string>>(@object.UploadFileAsync); return base.SendAsync<string>(request, fallback); } // Token: 0x06000009 RID: 9 RVA: 0x000030FC File Offset: 0x000012FC public Task<string> FormTestAsync(TestServiceParam A_1) { string text = "/Values/formTest"; string baseUrl = this.BaseUrl; string mappingUri = "/Values/formTest"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpRequestContent requestContent = new FeignClientHttpFormRequestContent<TestServiceParam>("param", A_1); MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.FormTestAsync(TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_2FEEC93AF4DF464F935172779B3FCB64 @object = new ITestService_2FEEC93AF4DF464F935172779B3FCB64(base.Fallback, A_1); Func<Task<string>> fallback = new Func<Task<string>>(@object.FormTestAsync); return base.SendAsync<string>(request, fallback); } // Token: 0x0600000A RID: 10 RVA: 0x00003188 File Offset: 0x00001388 public Task<string> UploadFilesAsync(IHttpRequestFile A_1, IHttpRequestFile A_2, IHttpRequestFile A_3) { string text = "/Values/uploadFiles"; string baseUrl = this.BaseUrl; string mappingUri = "/Values/uploadFiles"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpMultipartFormRequestContent feignClientHttpMultipartFormRequestContent = new FeignClientHttpMultipartFormRequestContent(); FeignClientHttpRequestContent content = new FeignClientHttpFileRequestContent("file1", A_1); feignClientHttpMultipartFormRequestContent.AddContent("file1", content); FeignClientHttpRequestContent content2 = new FeignClientHttpFileRequestContent("file2", A_2); feignClientHttpMultipartFormRequestContent.AddContent("file2", content2); FeignClientHttpRequestContent content3 = new FeignClientHttpFileRequestContent("file3", A_3); feignClientHttpMultipartFormRequestContent.AddContent("file3", content3); FeignClientHttpRequestContent requestContent = feignClientHttpMultipartFormRequestContent; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.UploadFilesAsync(IHttpRequestFile, IHttpRequestFile, IHttpRequestFile)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_BDAAB91BBC294D7A9A643C222FF7CDF0 @object = new ITestService_BDAAB91BBC294D7A9A643C222FF7CDF0(base.Fallback, A_1, A_2, A_3); Func<Task<string>> fallback = new Func<Task<string>>(@object.UploadFilesAsync); return base.SendAsync<string>(request, fallback); } // Token: 0x0600000B RID: 11 RVA: 0x00003274 File Offset: 0x00001474 public Task<QueryResult<JObject>> GetQueryResultValueAsync(string A_1, TestServiceParam A_2) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<string>(text, name, A_1); name = "param"; text = base.ReplaceRequestQuery<TestServiceParam>(text, name, A_2); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "GET"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.GetQueryResultValueAsync(string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_60B9BA54F7BE4F059E71A3FA0B0E99DA @object = new ITestService_60B9BA54F7BE4F059E71A3FA0B0E99DA(base.Fallback, A_1, A_2); Func<Task<QueryResult<JObject>>> fallback = new Func<Task<QueryResult<JObject>>>(@object.GetQueryResultValueAsync); return base.SendAsync<QueryResult<JObject>>(request, fallback); } // Token: 0x0600000C RID: 12 RVA: 0x00003320 File Offset: 0x00001520 public QueryResult<JObject> GetQueryResultValue(string A_1, TestServiceParam A_2) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<string>(text, name, A_1); name = "param"; text = base.ReplaceRequestQuery<TestServiceParam>(text, name, A_2); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "GET"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.GetQueryResultValue(string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_B53A91345B5F4218B3B164103A8ADC0D @object = new ITestService_B53A91345B5F4218B3B164103A8ADC0D(base.Fallback, A_1, A_2); Func<QueryResult<JObject>> fallback = new Func<QueryResult<JObject>>(@object.GetQueryResultValue); return base.Send<QueryResult<JObject>>(request, fallback); } // Token: 0x0600000D RID: 13 RVA: 0x000033CC File Offset: 0x000015CC public void GetValueVoid(int A_1, string A_2, TestServiceParam A_3) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "test"; text = base.ReplaceRequestParam<string>(text, name, A_2); name = "param"; text = base.ReplaceRequestQuery<TestServiceParam>(text, name, A_3); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "GET"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.GetValueVoid(int, string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_39637F2AC6C646519BA9CDBDA1A87290 @object = new ITestService_39637F2AC6C646519BA9CDBDA1A87290(base.Fallback, A_1, A_2, A_3); Action fallback = new Action(@object.GetValueVoid); base.Send(request, fallback); } // Token: 0x0600000E RID: 14 RVA: 0x00003490 File Offset: 0x00001690 public Task GetValueVoidAsync(int A_1, string A_2, TestServiceParam A_3) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "test"; text = base.ReplaceRequestParam<string>(text, name, A_2); name = "param"; text = base.ReplaceRequestQuery<TestServiceParam>(text, name, A_3); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "GET"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.GetValueVoidAsync(int, string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_3F68917A83FF4447A3D850B09069AA08 @object = new ITestService_3F68917A83FF4447A3D850B09069AA08(base.Fallback, A_1, A_2, A_3); Func<Task> fallback = new Func<Task>(@object.GetValueVoidAsync); return base.SendAsync(request, fallback); } // Token: 0x0600000F RID: 15 RVA: 0x00003554 File Offset: 0x00001754 public Task PostValueAsync(int A_1, string A_2, TestServiceParam A_3) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "test"; text = base.ReplaceRequestParam<string>(text, name, A_2); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpRequestContent requestContent = new FeignClientHttpJsonRequestContent<TestServiceParam>("param", A_3); MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.PostValueAsync(int, string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_B485C397DD544B24AA11792EE5E285A3 @object = new ITestService_B485C397DD544B24AA11792EE5E285A3(base.Fallback, A_1, A_2, A_3); Func<Task> fallback = new Func<Task>(@object.PostValueAsync); return base.SendAsync(request, fallback); } // Token: 0x06000010 RID: 16 RVA: 0x00003614 File Offset: 0x00001814 public Task PostValueFormAsync(int A_1, string A_2, TestServiceParam A_3) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "test"; text = base.ReplaceRequestParam<string>(text, name, A_2); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpRequestContent requestContent = new FeignClientHttpFormRequestContent<TestServiceParam>("param", A_3); MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.PostValueFormAsync(int, string, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_0B34E9A3AF144790983D48B04B98950B @object = new ITestService_0B34E9A3AF144790983D48B04B98950B(base.Fallback, A_1, A_2, A_3); Func<Task> fallback = new Func<Task>(@object.PostValueFormAsync); return base.SendAsync(request, fallback); } // Token: 0x06000011 RID: 17 RVA: 0x000036D4 File Offset: 0x000018D4 public Task PostValueForm2Async(int A_1, string A_2, TestServiceParam A_3, TestServiceParam A_4) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "test"; text = base.ReplaceRequestParam<string>(text, name, A_2); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "POST"; string contentType = null; FeignClientHttpMultipartFormRequestContent feignClientHttpMultipartFormRequestContent = new FeignClientHttpMultipartFormRequestContent(); FeignClientHttpRequestContent content = new FeignClientHttpFormRequestContent<TestServiceParam>("param1", A_3); feignClientHttpMultipartFormRequestContent.AddContent("param1", content); FeignClientHttpRequestContent content2 = new FeignClientHttpFormRequestContent<TestServiceParam>("param2", A_4); feignClientHttpMultipartFormRequestContent.AddContent("param2", content2); FeignClientHttpRequestContent requestContent = feignClientHttpMultipartFormRequestContent; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.PostValueForm2Async(int, string, TestServiceParam, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_231A4252DE2747128CCEE23E62E2CCE0 @object = new ITestService_231A4252DE2747128CCEE23E62E2CCE0(base.Fallback, A_1, A_2, A_3, A_4); Func<Task> fallback = new Func<Task>(@object.PostValueForm2Async); return base.SendAsync(request, fallback); } // Token: 0x06000012 RID: 18 RVA: 0x000037D0 File Offset: 0x000019D0 public void GetValueVoid(int A_1, TestServiceParam A_2, TestServiceParam A_3) { string text = "/{id}"; string name = "id"; text = base.ReplacePathVariable<int>(text, name, A_1); name = "queryParam"; text = base.ReplaceRequestParam<TestServiceParam>(text, name, A_2); name = "param"; text = base.ReplaceRequestQuery<TestServiceParam>(text, name, A_3); string baseUrl = this.BaseUrl; string mappingUri = "/{id}"; string uri = text; string httpMethod = "GET"; string contentType = null; FeignClientHttpRequestContent requestContent = null; MethodInfo method = null; if (base.FeignOptions.IncludeMethodMetadata) { method = methodof(ITestService.GetValueVoid(int, TestServiceParam, TestServiceParam)); } FeignClientHttpRequest request = new FeignClientHttpRequest(baseUrl, mappingUri, uri, httpMethod, contentType, requestContent, method); ITestService_A1609C56DF7E43209BF87431714D47CB @object = new ITestService_A1609C56DF7E43209BF87431714D47CB(base.Fallback, A_1, A_2, A_3); Action fallback = new Action(@object.GetValueVoid); base.Send(request, fallback); } }
接下来测试一下
[EditorBrowsable(EditorBrowsableState.Never)] public static class FeignBuilderExtensions { public static TFeignBuilder AddFeignClients<TFeignBuilder>(this TFeignBuilder feignBuilder, IFeignOptions options) where TFeignBuilder : IFeignBuilder { if (options.Assemblies.Count == 0) { feignBuilder.AddFeignClients(Assembly.GetEntryAssembly(), options.Lifetime); } else { foreach (var assembly in options.Assemblies) { feignBuilder.AddFeignClients(assembly, options.Lifetime); } } feignBuilder.AddLoggerFactory<DefaultLoggerFactory>(); feignBuilder.AddCacheProvider<DefaultCacheProvider>(); feignBuilder.AddServiceDiscovery<DefaultServiceDiscovery>(); feignBuilder.AddService<IFeignOptions>(options); return feignBuilder; } public static TFeignBuilder AddFeignClients<TFeignBuilder>(this TFeignBuilder feignBuilder, Assembly assembly, FeignClientLifetime lifetime) where TFeignBuilder : IFeignBuilder { if (assembly == null) { return feignBuilder; } foreach (var serviceType in assembly.GetTypes()) { FeignClientTypeInfo feignClientTypeInfo = feignBuilder.TypeBuilder.Build(serviceType); if (feignClientTypeInfo == null || feignClientTypeInfo.BuildType == null) { continue; } FeignClientAttribute feignClientAttribute = serviceType.GetCustomAttribute<FeignClientAttribute>(); feignBuilder.AddService(serviceType, feignClientTypeInfo.BuildType, feignClientAttribute.Lifetime ?? lifetime); // add fallback if (feignClientAttribute.Fallback != null) { feignBuilder.AddService(feignClientAttribute.Fallback, feignClientAttribute.Lifetime ?? lifetime); } if (feignClientAttribute.FallbackFactory != null) { feignBuilder.AddService(feignClientAttribute.FallbackFactory, feignClientAttribute.Lifetime ?? lifetime); } } return feignBuilder; } public static IFeignBuilder AddConverter<TSource, TResult>(this IFeignBuilder feignBuilder, IConverter<TSource, TResult> converter) { feignBuilder.Options.Converters.AddConverter(converter); return feignBuilder; } public static IFeignBuilder AddLoggerFactory<TLoggerFactory>(this IFeignBuilder feignBuilder) where TLoggerFactory : ILoggerFactory { feignBuilder.AddOrUpdateService(typeof(ILoggerFactory), typeof(TLoggerFactory), FeignClientLifetime.Singleton); return feignBuilder; } public static IFeignBuilder AddServiceDiscovery<TServiceDiscovery>(this IFeignBuilder feignBuilder) where TServiceDiscovery : IServiceDiscovery { feignBuilder.AddOrUpdateService(typeof(IServiceDiscovery), typeof(TServiceDiscovery), FeignClientLifetime.Singleton); return feignBuilder; } public static IFeignBuilder AddCacheProvider<TCacheProvider>(this IFeignBuilder feignBuilder) where TCacheProvider : ICacheProvider { feignBuilder.AddOrUpdateService(typeof(ICacheProvider), typeof(TCacheProvider), FeignClientLifetime.Singleton); return feignBuilder; } } [EditorBrowsable(EditorBrowsableState.Never)] public static class ServiceCollectionExtensions { public static IDependencyInjectionFeignBuilder AddFeignClients(this IServiceCollection services) { return AddFeignClients(services, (FeignOptions)null); } public static IDependencyInjectionFeignBuilder AddFeignClients(this IServiceCollection services, Action<IFeignOptions> setupAction) { FeignOptions options = new FeignOptions(); setupAction?.Invoke(options); return AddFeignClients(services, options); } public static IDependencyInjectionFeignBuilder AddFeignClients(this IServiceCollection services, IFeignOptions options) { if (options == null) { options = new FeignOptions(); } DependencyInjectionFeignBuilder feignBuilder = new DependencyInjectionFeignBuilder(); feignBuilder.Services = services; feignBuilder.Options = options; feignBuilder.AddFeignClients(options) .AddLoggerFactory<LoggerFactory>() .AddCacheProvider<CacheProvider>() ; return feignBuilder; } }
public static class FeignExtensions { public static IFeignBuilder AddTestFeignClients(this IFeignBuilder feignBuilder) { feignBuilder.AddServiceDiscovery<TestServiceDiscovery>(); feignBuilder.Options.IncludeMethodMetadata = true; feignBuilder.AddFeignClients(Assembly.GetExecutingAssembly(), FeignClientLifetime.Transient); feignBuilder.Options.FeignClientPipeline.Service<ITestService>().SendingRequest += (sender, e) => { //e.Terminate(); }; feignBuilder.Options.FeignClientPipeline.FallbackRequest += (sender, e) => { var parameters = e.GetParameters(); object fallback = e.Fallback; IFallbackProxy fallbackProxy = e.FallbackProxy; if (fallbackProxy == null) { string s = ""; } MethodInfo method = e.Method; e.Terminate(); }; feignBuilder.Options.FeignClientPipeline.Initializing += (sender, e) => { }; feignBuilder.Options.FeignClientPipeline.Service("yun-platform-service-provider").Initializing += (sender, e) => { }; feignBuilder.Options.FeignClientPipeline.Disposing += (sender, e) => { }; feignBuilder.Options.FeignClientPipeline.Authorization(proxy => { #if NETSTANDARD return ("global", "asdasd"); #else return new AuthenticationHeaderValue("global", "asdasd"); #endif }); feignBuilder.Options.FeignClientPipeline.BuildingRequest += FeignClientPipeline_BuildingRequest; feignBuilder.Options.FeignClientPipeline.Service<ITestService>().BuildingRequest += (sender, e) => { IFeignClient<ITestService> feignClient = e.FeignClient as IFeignClient<ITestService>; ITestService service = feignClient.Service; }; feignBuilder.Options.FeignClientPipeline.Service("yun-platform-service-provider").BuildingRequest += (sender, e) => { var fallbackFeignClient = e.FeignClient.AsFallback(); fallbackFeignClient = e.FeignClient.AsFallback<object>(); fallbackFeignClient = e.FeignClient.AsFallback<ITestService>(); var fallback = fallbackFeignClient?.Fallback; fallback = e.FeignClient.GetFallback(); fallback = e.FeignClient.GetFallback<object>(); // fallback = e.FeignClient.GetFallback<ITestService>(); if (!e.Headers.ContainsKey("Authorization")) { e.Headers["Authorization"] = "service asdasd"; } e.Headers["Accept-Encoding"] = "gzip, deflate, br"; //add session e.Headers.Add("cookie", "csrftoken=EGxYkyZeT3DxEsvYsdR5ncmzpi9pmnQx; _bl_uid=nLjRstOyqOejLv2s0xtzqs74Xsmg; courseId=1; versionId=522; textbookId=2598; Hm_lvt_f0984c42ef98965e03c60661581cd219=1559783251,1559818390,1560213044,1560396804; uuid=6a30ff68-2b7c-4cde-a355-2e332b74e31d##1; Hm_lpvt_f0984c42ef98965e03c60661581cd219=1560413345; SESSION=5ee4854d-34b7-423a-9cca-76ddc8a0f111; sid=5ee4854d-34b7-423a-9cca-76ddc8a0f111"); }; feignBuilder.Options.FeignClientPipeline.Service<ITestService>().Authorization(proxy => { #if NETSTANDARD return ("service", "asdasd"); #else return new AuthenticationHeaderValue("service", "asdasd"); #endif }); feignBuilder.Options.FeignClientPipeline.SendingRequest += FeignClientPipeline_SendingRequest; feignBuilder.Options.FeignClientPipeline.Service("yun-platform-service-provider").ReceivingResponse += (sender, e) => { }; feignBuilder.Options.FeignClientPipeline.ReceivingQueryResult(); feignBuilder.Options.FeignClientPipeline.CancelRequest += (sender, e) => { e.CancellationToken.Register((state) => { }, sender); }; feignBuilder.Options.FeignClientPipeline.ErrorRequest += (sender, e) => { Exception exception = e.Exception; //e.ExceptionHandled = true; }; return feignBuilder; } private static void FeignClientPipeline_BuildingRequest(object sender, IBuildingRequestEventArgs<object> e) { } private static void FeignClientPipeline_SendingRequest(object sender, ISendingRequestEventArgs<object> e) { //e.Terminate(); } public static void ReceivingQueryResult(this IGlobalFeignClientPipeline globalFeignClient) { globalFeignClient.ReceivingResponse += (sender, e) => { if (!typeof(QueryResult).IsAssignableFrom(e.ResultType)) { return; } if (e.ResultType == typeof(QueryResult)) { e.Result = new QueryResult() { StatusCode = e.ResponseMessage.StatusCode }; return; } if (e.ResultType.IsGenericType && e.ResultType.GetGenericTypeDefinition() == typeof(QueryResult<>)) { QueryResult queryResult; if (e.ResponseMessage.IsSuccessStatusCode) { string json = e.ResponseMessage.Content.ReadAsStringAsync().Result; object data = Newtonsoft.Json.JsonConvert.DeserializeObject(json, e.ResultType.GetGenericArguments()[0]); if (data == null) { queryResult = InvokeQueryResultConstructor(e.ResultType.GetGenericArguments()[0]); } else { queryResult = InvokeQueryResultConstructor(data.GetType(), data); } } else { queryResult = InvokeQueryResultConstructor(e.ResultType.GetGenericArguments()[0]); } queryResult.StatusCode = e.ResponseMessage.StatusCode; e.Result = queryResult; } }; } static readonly System.Collections.Concurrent.ConcurrentDictionary<Type, Func<object, QueryResult>> _newQueryResultMap = new System.Collections.Concurrent.ConcurrentDictionary<Type, Func<object, QueryResult>>(); static Func<QueryResult> _queryResultFunc; static QueryResult InvokeQueryResultConstructor(Type type, object value) { Func<object, QueryResult> func = _newQueryResultMap.GetOrAdd(type, key => { Type queryResultType = typeof(QueryResult<>).MakeGenericType(key); ConstructorInfo constructor = queryResultType.GetConstructor(new Type[] { key }); ParameterExpression parameter = Expression.Parameter(typeof(object)); NewExpression constructorExpression = Expression.New(constructor, Expression.Convert(parameter, key)); return Expression.Lambda<Func<object, QueryResult>>(constructorExpression, parameter).Compile(); }); return func.Invoke(value); } static QueryResult InvokeQueryResultConstructor(Type type) { if (_queryResultFunc == null) { Type queryResultType = typeof(QueryResult<>).MakeGenericType(type); ConstructorInfo constructor = queryResultType.GetConstructor(Type.EmptyTypes); NewExpression constructorExpression = Expression.New(constructor); _queryResultFunc = Expression.Lambda<Func<QueryResult>>(constructorExpression).Compile(); } return _queryResultFunc.Invoke(); } }
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // services.AddDiscoveryClient(Configuration); services.AddFeignClients() .AddTestFeignClients() //.AddSteeltoeServiceDiscovery() ; }
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // GET api/values/5 [HttpGet("{id}")] public async Task<ActionResult<object>> Get(int id, [FromServices] ITestService testService/*, [FromServices] ITestService1 testService1*/) { await testService.PostValueAsync(); //string html = await testService1.GetHtml(); //return html; //var rrr = typeof(Func<Task>).GetConstructors(System.Reflection.BindingFlags.Default); //IServiceCollection serviceCollection = HttpContext.RequestServices.GetService(typeof(IServiceCollection)) as IServiceCollection; //testService.GetValueVoidAsync(id, null, new TestServiceParam //{ // Name = "asasdsad" //}); //return await testService.GetValueAsync(id, "asdasd"); //await testService.PostValueForm2Async(id, "", new TestServiceParam //{ // Name = "testName" //}, new TestServiceParam //{ // Name = "name" //}); //testService.GetValueVoid(id, new TestServiceParam //{ // Name = "testName" //}, new TestServiceParam //{ // Name = "name" //}); //await testService.PostValueAsync(); //await testService.PostValueAsync(id, "", new TestServiceParam()); //return testService.GetQueryResultValue(id.ToString(), new TestServiceParam //{ // Name = "asasdsad" //}); //return await testService.GetQueryResultValueAsync(id.ToString(), new TestServiceParam //{ // Name = "asasdsad" //}); testService.GetValueVoidAsync(id, "", null); return "ok"; } }
正常工作了!
目前只支持简单的服务降级操作,没有实现Hystrix.
代码地址 : https://github.com/daixinkai/feign.net