之前因为工作原因使用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; } }

FeignClientAttribute

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 = ,
/// <summary>
/// Specifies that a new instance of the service will be created for each scope.
/// </summary>
Scoped = ,
/// <summary>
/// Specifies that a new instance of the service will be created every time it is
/// </summary>
Transient =
}

FeignClientLifetime

定义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;
}
}

RequestMappingAttribute

定义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
{
}

PathVariableAttribute等

为了方便扩展,定义一个管道,里面包含了服务实例初始化,发送前,发送后等的事件.

    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>();
}

FeignClientPipeline

基本的声明有了,然后开始实现动态生成代理类型代码.

先定义一个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 > )
{
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);
} }

ServiceDiscoveryHttpClientHandler

接下来开始生成代理类

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);
}

CreateTypeBuilder

构造函数

        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 = ; i <= baseConstructorInfo.GetParameters().Length; i++)
{
constructorIlGenerator.Emit(OpCodes.Ldarg_S, i);
}
constructorIlGenerator.Emit(OpCodes.Call, baseConstructorInfo);
constructorIlGenerator.Emit(OpCodes.Ret);
}

BuildConstructor

重写父类只读属性

 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);
}

BuildReadOnlyProperty

        //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[], 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 > )
{
throw new ArgumentException(nameof(requestMappingBaseAttributes.Length));
}
return requestMappingBaseAttributes[];
}
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 > && !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 > )
{
if (emitRequestContents.Count == )
{
if (typeof(IHttpRequestFile).IsAssignableFrom(emitRequestContents[].Parameter.ParameterType))
{
EmitFeignClientMultipartRequestContent(iLGenerator, emitRequestContents);
}
else
{
EmitFeignClientRequestContent(iLGenerator, emitRequestContents[], 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, );
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()[]);
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()[]);
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()[]);
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()[];
break;
case "application/x-www-form-urlencoded":
constructorInfo = typeof(FeignClientHttpFormRequestContent<>).MakeGenericType(emitRequestContent.Parameter.ParameterType).GetConstructors()[];
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()[]);
iLGenerator.Emit(OpCodes.Stloc, requestContent);
for (int i = ; 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 = ;
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 > && !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 = ;
foreach (BindingFlags item in Enum.GetValues(typeof(BindingFlags)))
{
bindingFlagsValue += item.GetHashCode();
}
var delegateConstructor = delegateType.GetConstructors((BindingFlags)bindingFlagsValue)[];
LocalBuilder invokeDelegate = iLGenerator.DeclareLocal(delegateType);
// if has parameters
if (method.GetParameters().Length > )
{
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 = ; 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); }

Service

生成的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);
}
}

Proxy Service

接下来测试一下

    [EditorBrowsable(EditorBrowsableState.Never)]
public static class FeignBuilderExtensions
{ public static TFeignBuilder AddFeignClients<TFeignBuilder>(this TFeignBuilder feignBuilder, IFeignOptions options) where TFeignBuilder : IFeignBuilder
{
if (options.Assemblies.Count == )
{
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;
} }

AddFeignClients

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()[]);
if (data == null)
{
queryResult = InvokeQueryResultConstructor(e.ResultType.GetGenericArguments()[]);
}
else
{
queryResult = InvokeQueryResultConstructor(data.GetType(), data);
}
}
else
{
queryResult = InvokeQueryResultConstructor(e.ResultType.GetGenericArguments()[]);
}
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();
} }

AddTestFeignClients

    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

打造适用于c#的feign的更多相关文章

  1. 性能1.84倍于Ceph!网易数帆Curve分布式存储开源

    在上周刚结束的网易数字+大会上 网易数帆宣布: 开源一款名为Curve的高性能分布式存储系统, 性能可达Ceph的1.84倍! 网易副总裁.网易杭州研究院执行院长兼网易数帆总经理汪源: 基础软件的能力 ...

  2. 打造通用的Android下拉刷新组件(适用于ListView、GridView等各类View)

    前言 近期在做项目时,使用了一个开源的下拉刷新ListView组件.极其的不稳定,bug还多.稳定的组件又写得太复杂了,jar包较大.在我的一篇博客中也讲述过下拉刷新的实现,即Android打造(Li ...

  3. 从0到1打造直播 App

    转自http://dev.qq.com/topic/5811d42e7fd6ec467453bf58 概要 分享内容: 互联网内容载体变迁历程,文字——图片/声音——视频——VR/AR——……..从直 ...

  4. 翻译-使用Ratpack和Spring Boot打造高性能的JVM微服务应用

    这是我为InfoQ翻译的文章,原文地址:Build High Performance JVM Microservices with Ratpack & Spring Boot,InfoQ上的中 ...

  5. 【腾讯Bugly干货分享】从0到1打造直播 App

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5811d42e7fd6ec467453bf58 作者:李智文 概要 分享内容: ...

  6. C#入门教程(二)–C#常用快捷键、变量、类型转换-打造C#学习教程

    C#入门教程(一)–.Net平台技术介绍.C#语言及开发工具介绍-打造C#学习教程 上次教程主要介绍了.Net平台以及C#语言的相关介绍.以及经典程序案例,helloworld程序. 初来乍到,第一次 ...

  7. [小技巧] 打造属于 Dell XPS 13 (9350) 的专属 Windows 7 iso 镜像

    MacBook Air 13, Dell XPS 13 和 Thinkpad X1 Carbon 都是轻薄笔记本中设计优秀的典范,受到很多用户追捧. 不过对于 Windows 阵营的笔记本,最近有个坏 ...

  8. (转)iOS Wow体验 - 第五章 - 利用iOS技术特性打造最佳体验

    本文是<iOS Wow Factor:Apps and UX Design Techniques for iPhone and iPad>第五章译文精选,其余章节将陆续放出.上一篇:Wow ...

  9. 挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务

    挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务#36氪开放日# 其他 JasonZheng • 2012-04-07 15:14 来自36氪开放 ...

随机推荐

  1. 下拉选择框,PopupWindow的使用

    实现下拉选择框 直接上代码 Activity.java package com.example.shaofei.customerviewdemo1; import android.os.Bundle; ...

  2. C#3.0新增功能09 LINQ 标准查询运算符 01 概述

    连载目录    [已更新最新开发文章,点击查看详细] 标准查询运算符 是组成 LINQ 模式的方法. 这些方法中的大多数都作用于序列:其中序列指其类型实现 IEnumerable<T> 接 ...

  3. vijos p1484 ISBN号码

    #include<iostream>#include<string>#include<cctype>using namespace std;int main() { ...

  4. web页面保存图片到本地

    web页生成分享海报功能踩坑经验 https://blog.csdn.net/candy_home/article/details/78424642 https://www.jianshu.com/p ...

  5. Java:前程似锦的 NIO 2.0

    Java 之所以能够霸占编程语言的榜首,其强大.丰富的类库功不可没,几乎所有的编程问题都能在其中找到解决方案.但在早期的版本当中,输入输出(I/O)流并不那么令开发者感到愉快: 1)JDK 1.4 之 ...

  6. Linux 安装MySql——apt-get版

    0)apt-get update 1)通过apt-get安装 更新设置到最新系统:    sudo apt-get update    sudo apt-get upgrade sudo apt-ge ...

  7. Linux C 网络编程——多线程的聊天室实现(服务器端)

    服务器端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人,可进行更改),每个人所发送的消息其他用户均可以收到.用户可以随意的加入或退出(推出以字符串"bye"实现),服 ...

  8. CentOS虚拟机查询jdk路径

    [root@wshCentOS ~]# which java/usr/bin/java[root@wshCentOS ~]# ls -lrt /usr/bin/javalrwxrwxrwx. 1 ro ...

  9. linux初学者-MariaDB图形管理篇

     linux初学者-MariaDB图形管理篇 MariaDB不仅有文本管理方式,也有借助工具的图形管理方式.其图形管理的工具是"phpmyadmin".这个软件可以在"p ...

  10. springBoot-mongodb

    作者:纯洁的微笑出处:http://www.ityouknow.com/ 版权归作者所有,转载请注明出处 mongodb是最早热门非关系数据库的之一,使用也比较普遍,一般会用做离线数据分析来使用,放到 ...