From 69bea7324c1f77d1673e09096177c93268aa912e Mon Sep 17 00:00:00 2001 From: Anthony Stivers Date: Sat, 16 Feb 2019 19:14:46 -0500 Subject: [PATCH 1/3] Trim trailing `/` from hostUrl --- Refit.Tests/InterfaceStubGenerator.cs | 2 +- Refit.Tests/RefitStubs.Net46.cs | 32 +++++++++++++++++++++++++++ Refit.Tests/RefitStubs.NetCore2.cs | 32 +++++++++++++++++++++++++++ Refit.Tests/RestService.cs | 19 ++++++++++++++++ Refit/RestService.cs | 2 +- 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/Refit.Tests/InterfaceStubGenerator.cs b/Refit.Tests/InterfaceStubGenerator.cs index 12bea541e..25ecca9cf 100644 --- a/Refit.Tests/InterfaceStubGenerator.cs +++ b/Refit.Tests/InterfaceStubGenerator.cs @@ -123,7 +123,7 @@ public void GenerateTemplateInfoForInterfaceListSmokeTest() .ToList(); var result = fixture.GenerateTemplateInfoForInterfaceList(input); - Assert.Equal(10, result.ClassList.Count); + Assert.Equal(11, result.ClassList.Count); } [Fact] diff --git a/Refit.Tests/RefitStubs.Net46.cs b/Refit.Tests/RefitStubs.Net46.cs index 85a1f2dfa..68e2ec8e4 100644 --- a/Refit.Tests/RefitStubs.Net46.cs +++ b/Refit.Tests/RefitStubs.Net46.cs @@ -1083,6 +1083,38 @@ public virtual Task> GetRemoteFileWithMetadata(string filena } } +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 AutoGeneratedITrimTrailingForwardSlashApi : ITrimTrailingForwardSlashApi + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedITrimTrailingForwardSlashApi(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + public virtual Task 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 85a1f2dfa..68e2ec8e4 100644 --- a/Refit.Tests/RefitStubs.NetCore2.cs +++ b/Refit.Tests/RefitStubs.NetCore2.cs @@ -1083,6 +1083,38 @@ public virtual Task> GetRemoteFileWithMetadata(string filena } } +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 AutoGeneratedITrimTrailingForwardSlashApi : ITrimTrailingForwardSlashApi + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedITrimTrailingForwardSlashApi(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + public virtual Task 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 c09fbc3d9..2e5c7639a 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -131,6 +131,14 @@ public interface IBodylessApi Task Head(); } + public interface ITrimTrailingForwardSlashApi + { + HttpClient Client { get; } + + [Get("/someendpoint")] + Task Get(); + } + public class HttpBinGet { public Dictionary Args { get; set; } @@ -1187,5 +1195,16 @@ public async Task CanSerializeContentAsXml() mockHttp.VerifyNoOutstandingExpectation(); } + + [Fact] + public void ShouldTrimTrailingForwardSlashFromBaseUrl() + { + var expectedBaseAddress = "http://example.com/api"; + var inputBaseAddress = "http://example.com/api/"; + + var fixture = RestService.For(inputBaseAddress); + + Assert.Equal(fixture.Client.BaseAddress.AbsoluteUri, expectedBaseAddress); + } } } diff --git a/Refit/RestService.cs b/Refit/RestService.cs index 82f86af2d..0ea08e3e1 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -49,7 +49,7 @@ public static T For(string hostUrl, RefitSettings settings) } } - var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl) }; + var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; return For(client, settings); } From 4c864fe53dd38d2957d9a4c69e82b95e2e174a2f Mon Sep 17 00:00:00 2001 From: Anthony Stivers Date: Sat, 16 Feb 2019 19:56:32 -0500 Subject: [PATCH 2/3] Use null-conditional operator to prevent NRE --- Refit/RestService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refit/RestService.cs b/Refit/RestService.cs index 0ea08e3e1..fb5f3df28 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -49,7 +49,7 @@ public static T For(string hostUrl, RefitSettings settings) } } - var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; + var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl?.TrimEnd('/')) }; return For(client, settings); } From 89d9db7b525cbc0b6520d317e49dedd35f45b881 Mon Sep 17 00:00:00 2001 From: Anthony Stivers Date: Sat, 16 Feb 2019 20:00:53 -0500 Subject: [PATCH 3/3] ArgumentException if hostUrl IsNullOrWhitespace --- Refit.Tests/InterfaceStubGenerator.cs | 2 +- Refit.Tests/RefitStubs.Net46.cs | 32 ++++++++++++++++ Refit.Tests/RefitStubs.NetCore2.cs | 32 ++++++++++++++++ Refit.Tests/RestService.cs | 54 +++++++++++++++++++++++++++ Refit/RestService.cs | 9 ++++- 5 files changed, 127 insertions(+), 2 deletions(-) diff --git a/Refit.Tests/InterfaceStubGenerator.cs b/Refit.Tests/InterfaceStubGenerator.cs index 25ecca9cf..1b6a62bac 100644 --- a/Refit.Tests/InterfaceStubGenerator.cs +++ b/Refit.Tests/InterfaceStubGenerator.cs @@ -123,7 +123,7 @@ public void GenerateTemplateInfoForInterfaceListSmokeTest() .ToList(); var result = fixture.GenerateTemplateInfoForInterfaceList(input); - Assert.Equal(11, result.ClassList.Count); + Assert.Equal(12, result.ClassList.Count); } [Fact] diff --git a/Refit.Tests/RefitStubs.Net46.cs b/Refit.Tests/RefitStubs.Net46.cs index 68e2ec8e4..fda73a626 100644 --- a/Refit.Tests/RefitStubs.Net46.cs +++ b/Refit.Tests/RefitStubs.Net46.cs @@ -1241,3 +1241,35 @@ public virtual Task Get(int httpstatuscode) } } } + +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 AutoGeneratedIValidApi : IValidApi + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIValidApi(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + public virtual Task Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} diff --git a/Refit.Tests/RefitStubs.NetCore2.cs b/Refit.Tests/RefitStubs.NetCore2.cs index 68e2ec8e4..fda73a626 100644 --- a/Refit.Tests/RefitStubs.NetCore2.cs +++ b/Refit.Tests/RefitStubs.NetCore2.cs @@ -1241,3 +1241,35 @@ public virtual Task Get(int httpstatuscode) } } } + +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 AutoGeneratedIValidApi : IValidApi + { + /// + public HttpClient Client { get; protected set; } + readonly IRequestBuilder requestBuilder; + + /// + public AutoGeneratedIValidApi(HttpClient client, IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + /// + public virtual Task Get() + { + var arguments = new object[] { }; + var func = requestBuilder.BuildRestResultFuncForMethod("Get", new Type[] { }); + return (Task)func(Client, arguments); + } + } +} diff --git a/Refit.Tests/RestService.cs b/Refit.Tests/RestService.cs index 2e5c7639a..710f8a250 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -139,6 +139,12 @@ public interface ITrimTrailingForwardSlashApi Task Get(); } + public interface IValidApi + { + [Get("/someendpoint")] + Task Get(); + } + public class HttpBinGet { public Dictionary Args { get; set; } @@ -1206,5 +1212,53 @@ public void ShouldTrimTrailingForwardSlashFromBaseUrl() Assert.Equal(fixture.Client.BaseAddress.AbsoluteUri, expectedBaseAddress); } + + [Fact] + public void ShouldThrowArgumentExceptionIfHostUrlIsNull() + { + try + { + RestService.For(hostUrl: null); + } + catch (ArgumentException ex) + { + Assert.Equal("hostUrl", ex.ParamName); + return; + } + + Assert.False(true, "Exception not thrown."); + } + + [Fact] + public void ShouldThrowArgumentExceptionIfHostUrlIsEmpty() + { + try + { + RestService.For(hostUrl: ""); + } + catch (ArgumentException ex) + { + Assert.Equal("hostUrl", ex.ParamName); + return; + } + + Assert.False(true, "Exception not thrown."); + } + + [Fact] + public void ShouldThrowArgumentExceptionIfHostUrlIsWhitespace() + { + try + { + RestService.For(hostUrl: " "); + } + catch (ArgumentException ex) + { + Assert.Equal("hostUrl", ex.ParamName); + return; + } + + Assert.False(true, "Exception not thrown."); + } } } diff --git a/Refit/RestService.cs b/Refit/RestService.cs index fb5f3df28..d219b842a 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -34,6 +34,13 @@ public static T For(HttpClient client, RefitSettings settings) public static T For(string hostUrl, RefitSettings settings) { + if (string.IsNullOrWhiteSpace(hostUrl)) + { + throw new ArgumentException( + $"`{nameof(hostUrl)}` must not be null or whitespace.", + nameof(hostUrl)); + } + // check to see if user provided custom auth token HttpMessageHandler innerHandler = null; if (settings != null) @@ -49,7 +56,7 @@ public static T For(string hostUrl, RefitSettings settings) } } - var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl?.TrimEnd('/')) }; + var client = new HttpClient(innerHandler ?? new HttpClientHandler()) { BaseAddress = new Uri(hostUrl.TrimEnd('/')) }; return For(client, settings); }