From d6570f9fe84613daf2c57bbb44bb99cc064d4c12 Mon Sep 17 00:00:00 2001 From: "yuri.voloshyn" Date: Tue, 2 Apr 2019 18:47:58 +0300 Subject: [PATCH 1/2] Added non generic methods for create rest service --- .../InterfaceStubGenerator.cs | 7 +-- .../HttpClientFactoryExtensions.cs | 16 +++++- Refit.Tests/RestService.cs | 11 ++++ Refit/CachedRequestBuilderImplementation.cs | 13 +++-- Refit/RequestBuilder.cs | 10 ++++ Refit/RequestBuilderFactory.cs | 6 +++ ...tBuilderImplementation.TaskToObservable.cs | 2 +- Refit/RequestBuilderImplementation.cs | 20 ++++--- Refit/RestService.cs | 53 ++++++++++++++++--- Refit/UniqueName.cs | 21 +++++--- 10 files changed, 126 insertions(+), 33 deletions(-) diff --git a/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs b/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs index 942de5753..54292bfd8 100644 --- a/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs +++ b/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs @@ -373,7 +373,7 @@ public class ClassTemplateInfo public string TypeParameters => TypeParametersInfo != null ? string.Join(", ", TypeParametersInfo) : null; } - public class TypeInfo : ICloneable + public class TypeInfo { public string Name { get; set; } public List Children { get; set; } @@ -388,11 +388,6 @@ public TypeInfo Clone() return CloneImpl() as TypeInfo; } - object ICloneable.Clone() - { - throw new NotImplementedException(); - } - protected virtual object CloneImpl() { return new TypeInfo diff --git a/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs b/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs index e94403f5b..dcb63d8b3 100644 --- a/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs +++ b/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; namespace Refit { @@ -18,5 +19,18 @@ public static IHttpClientBuilder AddRefitClient(this IServiceCollection servi return services.AddHttpClient(UniqueName.ForType()) .AddTypedClient((client, serviceProvider) => RestService.For(client, serviceProvider.GetService>())); } + + /// + /// Adds a Refit client to the DI container + /// + /// container + /// Type of the Refit interface + /// Optional. Settings to configure the instance with + /// + public static IHttpClientBuilder AddRefitClient(this IServiceCollection services, Type refitInterfaceType, RefitSettings settings = null) + { + return services.AddHttpClient(UniqueName.ForType(refitInterfaceType)) + .AddTypedClient((client, serviceProvider) => RestService.For(refitInterfaceType, client, settings)); + } } } diff --git a/Refit.Tests/RestService.cs b/Refit.Tests/RestService.cs index ab8da5111..4eeb42189 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -1290,5 +1290,16 @@ public void ShouldThrowArgumentExceptionIfHostUrlIsWhitespace() Assert.False(true, "Exception not thrown."); } + + [Fact] + public void NonGenericCreate() + { + var expectedBaseAddress = "http://example.com/api"; + var inputBaseAddress = "http://example.com/api/"; + + var fixture = RestService.For(typeof(ITrimTrailingForwardSlashApi), inputBaseAddress) as ITrimTrailingForwardSlashApi; + + Assert.Equal(fixture.Client.BaseAddress.AbsoluteUri, expectedBaseAddress); + } } } diff --git a/Refit/CachedRequestBuilderImplementation.cs b/Refit/CachedRequestBuilderImplementation.cs index c3a00d4eb..8436eaae6 100644 --- a/Refit/CachedRequestBuilderImplementation.cs +++ b/Refit/CachedRequestBuilderImplementation.cs @@ -5,14 +5,21 @@ namespace Refit { - class CachedRequestBuilderImplementation : IRequestBuilder + class CachedRequestBuilderImplementation : CachedRequestBuilderImplementation, IRequestBuilder { - public CachedRequestBuilderImplementation(IRequestBuilder innerBuilder) + public CachedRequestBuilderImplementation(IRequestBuilder innerBuilder) : base(innerBuilder) + { + } + } + + class CachedRequestBuilderImplementation : IRequestBuilder + { + public CachedRequestBuilderImplementation(IRequestBuilder innerBuilder) { this.innerBuilder = innerBuilder; } - readonly IRequestBuilder innerBuilder; + readonly IRequestBuilder innerBuilder; readonly ConcurrentDictionary> methodDictionary = new ConcurrentDictionary>(); public Func BuildRestResultFuncForMethod(string methodName, Type[] parameterTypes = null, Type[] genericArgumentTypes = null) diff --git a/Refit/RequestBuilder.cs b/Refit/RequestBuilder.cs index af4ec68b2..afb91ce04 100644 --- a/Refit/RequestBuilder.cs +++ b/Refit/RequestBuilder.cs @@ -28,5 +28,15 @@ public static IRequestBuilder ForType() { return PlatformRequestBuilderFactory.Create(null); } + + public static IRequestBuilder ForType(Type refitInterfaceType, RefitSettings settings) + { + return PlatformRequestBuilderFactory.Create(refitInterfaceType, settings); + } + + public static IRequestBuilder ForType(Type refitInterfaceType) + { + return PlatformRequestBuilderFactory.Create(refitInterfaceType, null); + } } } diff --git a/Refit/RequestBuilderFactory.cs b/Refit/RequestBuilderFactory.cs index d59d0967c..8684d96e5 100644 --- a/Refit/RequestBuilderFactory.cs +++ b/Refit/RequestBuilderFactory.cs @@ -8,6 +8,7 @@ namespace Refit interface IRequestBuilderFactory { IRequestBuilder Create(RefitSettings settings); + IRequestBuilder Create(Type refitInterfaceType, RefitSettings settings); } class RequestBuilderFactory : IRequestBuilderFactory @@ -16,5 +17,10 @@ public IRequestBuilder Create(RefitSettings settings = null) { return new CachedRequestBuilderImplementation(new RequestBuilderImplementation(settings)); } + + public IRequestBuilder Create(Type refitInterfaceType, RefitSettings settings = null) + { + return new CachedRequestBuilderImplementation(new RequestBuilderImplementation(refitInterfaceType, settings)); + } } } diff --git a/Refit/RequestBuilderImplementation.TaskToObservable.cs b/Refit/RequestBuilderImplementation.TaskToObservable.cs index 071d3afd8..2269c1356 100644 --- a/Refit/RequestBuilderImplementation.TaskToObservable.cs +++ b/Refit/RequestBuilderImplementation.TaskToObservable.cs @@ -6,7 +6,7 @@ namespace Refit { - partial class RequestBuilderImplementation + partial class RequestBuilderImplementation { sealed class TaskToObservable : IObservable { diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index 3615e2b9e..aa3a65950 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -15,7 +15,14 @@ namespace Refit { - partial class RequestBuilderImplementation : IRequestBuilder + class RequestBuilderImplementation : RequestBuilderImplementation, IRequestBuilder + { + public RequestBuilderImplementation(RefitSettings refitSettings = null) : base(typeof(TApi), refitSettings) + { + } + } + + partial class RequestBuilderImplementation : IRequestBuilder { static readonly ISet BodylessMethods = new HashSet { @@ -28,25 +35,24 @@ partial class RequestBuilderImplementation : IRequestBuilder readonly RefitSettings settings; public Type TargetType { get; } - public RequestBuilderImplementation(RefitSettings refitSettings = null) + public RequestBuilderImplementation(Type refitInterfaceType, RefitSettings refitSettings = null) { - Type targetInterface = typeof(TApi); - Type[] targetInterfaceInheritedInterfaces = targetInterface.GetInterfaces(); + Type[] targetInterfaceInheritedInterfaces = refitInterfaceType.GetInterfaces(); settings = refitSettings ?? new RefitSettings(); serializer = settings.ContentSerializer; interfaceGenericHttpMethods = new ConcurrentDictionary(); - if (targetInterface == null || !targetInterface.GetTypeInfo().IsInterface) + if (refitInterfaceType == null || !refitInterfaceType.GetTypeInfo().IsInterface) { throw new ArgumentException("targetInterface must be an Interface"); } - TargetType = targetInterface; + TargetType = refitInterfaceType; var dict = new Dictionary>(); - AddInterfaceHttpMethods(targetInterface, dict); + AddInterfaceHttpMethods(refitInterfaceType, dict); foreach (var inheritedInterface in targetInterfaceInheritedInterfaces) { AddInterfaceHttpMethods(inheritedInterface, dict); diff --git a/Refit/RestService.cs b/Refit/RestService.cs index d219b842a..6d2e282a7 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -25,14 +25,49 @@ public static T For(HttpClient client, IRequestBuilder builder) public static T For(HttpClient client, RefitSettings settings) { - IRequestBuilder requestBuilder = RequestBuilder.ForType(settings); + var requestBuilder = RequestBuilder.ForType(settings); - return For(client, requestBuilder); + return For(client, requestBuilder); } public static T For(HttpClient client) => For(client, (RefitSettings)null); public static T For(string hostUrl, RefitSettings settings) + { + var client = CreateHttpClient(hostUrl, settings); + + return For(client, settings); + } + + public static T For(string hostUrl) => For(hostUrl, null); + + public static object For(Type refitInterfaceType, HttpClient client, IRequestBuilder builder) + { + var generatedType = TypeMapping.GetOrAdd(refitInterfaceType, GetGeneratedType(refitInterfaceType)); + + return Activator.CreateInstance(generatedType, client, builder); + } + + public static object For(Type refitInterfaceType, HttpClient client, RefitSettings settings) + { + var requestBuilder = RequestBuilder.ForType(refitInterfaceType, settings); + + return For(refitInterfaceType, client, requestBuilder); + } + + public static object For(Type refitInterfaceType, HttpClient client) => For(refitInterfaceType, client, (RefitSettings)null); + + public static object For(Type refitInterfaceType, string hostUrl, RefitSettings settings) + { + var client = CreateHttpClient(hostUrl, settings); + + return For(refitInterfaceType, client, settings); + } + + public static object For(Type refitInterfaceType, string hostUrl) => For(refitInterfaceType, hostUrl, null); + + + public static HttpClient CreateHttpClient(string hostUrl, RefitSettings settings) { if (string.IsNullOrWhiteSpace(hostUrl)) { @@ -56,21 +91,23 @@ public static T For(string hostUrl, RefitSettings settings) } } - var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; - return For(client, settings); + return new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; } - public static T For(string hostUrl) => For(hostUrl, null); - static Type GetGeneratedType() { - string typeName = UniqueName.ForType(); + return GetGeneratedType(typeof(T)); + } + + static Type GetGeneratedType(Type refitInterfaceType) + { + string typeName = UniqueName.ForType(refitInterfaceType); var generatedType = Type.GetType(typeName); if (generatedType == null) { - var message = typeof(T).Name + " doesn't look like a Refit interface. Make sure it has at least one " + "method with a Refit HTTP method attribute and Refit is installed in the project."; + var message = refitInterfaceType.Name + " doesn't look like a Refit interface. Make sure it has at least one " + "method with a Refit HTTP method attribute and Refit is installed in the project."; throw new InvalidOperationException(message); } diff --git a/Refit/UniqueName.cs b/Refit/UniqueName.cs index 33e6d0082..2317ba860 100644 --- a/Refit/UniqueName.cs +++ b/Refit/UniqueName.cs @@ -1,26 +1,33 @@ -namespace Refit +using System; + +namespace Refit { internal class UniqueName { public static string ForType() + { + return ForType(typeof(T)); + } + + public static string ForType(Type refitInterfaceType) { string typeName; - if (typeof(T).IsNested) + if (refitInterfaceType.IsNested) { - var className = "AutoGenerated" + typeof(T).DeclaringType.Name + typeof(T).Name; - typeName = typeof(T).AssemblyQualifiedName.Replace(typeof(T).DeclaringType.FullName + "+" + typeof(T).Name, typeof(T).Namespace + "." + className); + var className = "AutoGenerated" + refitInterfaceType.DeclaringType.Name + refitInterfaceType.Name; + typeName = refitInterfaceType.AssemblyQualifiedName.Replace(refitInterfaceType.DeclaringType.FullName + "+" + refitInterfaceType.Name, refitInterfaceType.Namespace + "." + className); } else { - var className = "AutoGenerated" + typeof(T).Name; + var className = "AutoGenerated" + refitInterfaceType.Name; - if (typeof(T).Namespace == null) + if (refitInterfaceType.Namespace == null) { className = $"{className}.{className}"; } - typeName = typeof(T).AssemblyQualifiedName.Replace(typeof(T).Name, className); + typeName = refitInterfaceType.AssemblyQualifiedName.Replace(refitInterfaceType.Name, className); } return typeName; From ddcced27142e201625b9566aadc6a266d744864d Mon Sep 17 00:00:00 2001 From: "yuri.voloshyn" Date: Tue, 2 Apr 2019 19:25:38 +0300 Subject: [PATCH 2/2] Fix find method --- Refit/RequestBuilderImplementation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index aa3a65950..c6b7f0b61 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -158,7 +158,7 @@ public Func BuildRestResultFuncForMethod(string me // difficult to upcast Task to an arbitrary T, especially // if you need to AOT everything, so we need to reflectively // invoke buildTaskFuncForMethod. - var taskFuncMi = GetType().GetMethod(nameof(BuildTaskFuncForMethod), BindingFlags.NonPublic | BindingFlags.Instance); + var taskFuncMi = typeof(RequestBuilderImplementation).GetMethod(nameof(BuildTaskFuncForMethod), BindingFlags.NonPublic | BindingFlags.Instance); var taskFunc = (MulticastDelegate)(restMethod.IsApiResponse ? taskFuncMi.MakeGenericMethod(restMethod.SerializedReturnType, restMethod.SerializedGenericArgument) : taskFuncMi.MakeGenericMethod(restMethod.SerializedReturnType, restMethod.SerializedReturnType)).Invoke(this, new[] { restMethod }); @@ -167,7 +167,7 @@ public Func BuildRestResultFuncForMethod(string me } // Same deal - var rxFuncMi = GetType().GetMethod(nameof(BuildRxFuncForMethod), BindingFlags.NonPublic | BindingFlags.Instance); + var rxFuncMi = typeof(RequestBuilderImplementation).GetMethod(nameof(BuildRxFuncForMethod), BindingFlags.NonPublic | BindingFlags.Instance); var rxFunc = (MulticastDelegate)(restMethod.IsApiResponse ? rxFuncMi.MakeGenericMethod(restMethod.SerializedReturnType, restMethod.SerializedGenericArgument) : rxFuncMi.MakeGenericMethod(restMethod.SerializedReturnType, restMethod.SerializedReturnType)).Invoke(this, new[] { restMethod });