diff --git a/Refit.Tests/InterfaceStubGenerator.cs b/Refit.Tests/InterfaceStubGenerator.cs index 3ca27f283..2e0858485 100644 --- a/Refit.Tests/InterfaceStubGenerator.cs +++ b/Refit.Tests/InterfaceStubGenerator.cs @@ -126,7 +126,7 @@ public void GenerateTemplateInfoForInterfaceListSmokeTest() .ToList(); var result = fixture.GenerateTemplateInfoForInterfaceList(input); - Assert.Equal(12, result.ClassList.Count); + Assert.Equal(14, result.ClassList.Count); } [Fact] diff --git a/Refit.Tests/RefitStubs.Net46.cs b/Refit.Tests/RefitStubs.Net46.cs index 64751d66b..06826898a 100644 --- a/Refit.Tests/RefitStubs.Net46.cs +++ b/Refit.Tests/RefitStubs.Net46.cs @@ -1331,6 +1331,70 @@ Task INpmJs.GetCongruence() } } +namespace Refit.Tests +{ + using Refit.Tests.RefitInternalGenerated; + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [Preserve] + [global::System.Reflection.Obfuscation(Exclude=true)] + partial class AutoGeneratedIRelativePathApi1 : IRelativePathApi1 + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIRelativePathApi1(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + Task IRelativePathApi1.Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} + +namespace Refit.Tests +{ + using Refit.Tests.RefitInternalGenerated; + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [Preserve] + [global::System.Reflection.Obfuscation(Exclude=true)] + partial class AutoGeneratedIRelativePathApi2 : IRelativePathApi2 + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIRelativePathApi2(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + Task IRelativePathApi2.Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} + namespace Refit.Tests { using Refit.Tests.RefitInternalGenerated; diff --git a/Refit.Tests/RefitStubs.NetCore2.cs b/Refit.Tests/RefitStubs.NetCore2.cs index 64751d66b..06826898a 100644 --- a/Refit.Tests/RefitStubs.NetCore2.cs +++ b/Refit.Tests/RefitStubs.NetCore2.cs @@ -1331,6 +1331,70 @@ Task INpmJs.GetCongruence() } } +namespace Refit.Tests +{ + using Refit.Tests.RefitInternalGenerated; + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [Preserve] + [global::System.Reflection.Obfuscation(Exclude=true)] + partial class AutoGeneratedIRelativePathApi1 : IRelativePathApi1 + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIRelativePathApi1(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + Task IRelativePathApi1.Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} + +namespace Refit.Tests +{ + using Refit.Tests.RefitInternalGenerated; + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [Preserve] + [global::System.Reflection.Obfuscation(Exclude=true)] + partial class AutoGeneratedIRelativePathApi2 : IRelativePathApi2 + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIRelativePathApi2(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + Task IRelativePathApi2.Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} + namespace Refit.Tests { using Refit.Tests.RefitInternalGenerated; diff --git a/Refit.Tests/RestService.cs b/Refit.Tests/RestService.cs index 4eeb42189..6efb7e8c1 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -139,6 +139,24 @@ public interface ITrimTrailingForwardSlashApi Task Get(); } + [BaseAddress("/api/Test")] + public interface IRelativePathApi1 + { + HttpClient Client { get; } + + [Get("/someendpoint")] + Task Get(); + } + + [BaseAddress()] + public interface IRelativePathApi2 + { + HttpClient Client { get; } + + [Get("/someendpoint")] + Task Get(); + } + public interface IValidApi { [Get("/someendpoint")] @@ -1301,5 +1319,20 @@ public void NonGenericCreate() Assert.Equal(fixture.Client.BaseAddress.AbsoluteUri, expectedBaseAddress); } + + [Fact] + public void RelativePathCreate() + { + var inputBaseAddress = "http://example.com/"; + + var expectedBaseAddress1 = "http://example.com/api/Test"; + var expectedBaseAddress2 = "http://example.com/"; + + var fixture1 = RestService.For(inputBaseAddress); + var fixture2 = RestService.For(inputBaseAddress); + + Assert.Equal(fixture1.Client.BaseAddress.AbsoluteUri, expectedBaseAddress1); + Assert.Equal(fixture2.Client.BaseAddress.AbsoluteUri, expectedBaseAddress2); + } } } diff --git a/Refit/Attributes.cs b/Refit/Attributes.cs index f7097ece6..afcaa7300 100644 --- a/Refit/Attributes.cs +++ b/Refit/Attributes.cs @@ -318,4 +318,15 @@ public QueryAttribute(CollectionFormat collectionFormat) /// public CollectionFormat CollectionFormat { get; set; } = CollectionFormat.RefitParameterFormatter; } + + [AttributeUsage(AttributeTargets.Interface)] + public class BaseAddressAttribute : Attribute + { + public BaseAddressAttribute(string relativePath = null) + { + RelativePath = relativePath; + } + + public string RelativePath { get; } + } } diff --git a/Refit/RestService.cs b/Refit/RestService.cs index 6d2e282a7..41ef3f2b4 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Reflection; using System.Threading.Tasks; namespace Refit @@ -18,9 +19,7 @@ public static class RestService public static T For(HttpClient client, IRequestBuilder builder) { - var generatedType = TypeMapping.GetOrAdd(typeof(T), GetGeneratedType()); - - return (T)Activator.CreateInstance(generatedType, client, builder); + return (T)For(typeof(T), client, builder); } public static T For(HttpClient client, RefitSettings settings) @@ -43,7 +42,14 @@ public static T For(string hostUrl, RefitSettings settings) public static object For(Type refitInterfaceType, HttpClient client, IRequestBuilder builder) { - var generatedType = TypeMapping.GetOrAdd(refitInterfaceType, GetGeneratedType(refitInterfaceType)); + var generatedType = TypeMapping.GetOrAdd(refitInterfaceType, type => GetGeneratedType(type)); + + var aps = refitInterfaceType.GetTypeInfo().GetCustomAttributes(true).OfType().FirstOrDefault(); + + if (aps != null && !string.IsNullOrEmpty(aps.RelativePath)) + { + client.BaseAddress = new Uri(client.BaseAddress, aps.RelativePath); + } return Activator.CreateInstance(generatedType, client, builder); } @@ -94,14 +100,9 @@ public static HttpClient CreateHttpClient(string hostUrl, RefitSettings settings return new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; } - static Type GetGeneratedType() - { - return GetGeneratedType(typeof(T)); - } - static Type GetGeneratedType(Type refitInterfaceType) { - string typeName = UniqueName.ForType(refitInterfaceType); + var typeName = UniqueName.ForType(refitInterfaceType); var generatedType = Type.GetType(typeName);