Skip to content

Commit

Permalink
Revert "Feature: allow developers to inject the MethodInfo as a Prope…
Browse files Browse the repository at this point in the history
…rty (#1367)"

This reverts commit b06ef7c.
  • Loading branch information
anaisbetts committed Apr 17, 2023
1 parent 772c6a0 commit 2a79e02
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 177 deletions.
50 changes: 1 addition & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ services
* [Removing headers](#removing-headers)
* [Passing state into DelegatingHandlers](#passing-state-into-delegatinghandlers)
* [Support for Polly and Polly.Context](#support-for-polly-and-pollycontext)
* [Target Interface type and method info](#target-interface-type-and-method-info)
* [Target Interface type](#target-interface-type)
* [MethodInfo of the method on the Refit client interface that was invoked](#methodinfo-of-the-method-on-the-refit-client-interface-that-was-invoked)
* [Multipart uploads](#multipart-uploads)
* [Retrieving the response](#retrieving-the-response)
Expand Down Expand Up @@ -882,54 +882,6 @@ class RequestPropertyHandler : DelegatingHandler

Note: in .NET 5 `HttpRequestMessage.Properties` has been marked `Obsolete` and Refit will instead populate the value into the new `HttpRequestMessage.Options`. Refit provides `HttpRequestMessageOptions.InterfaceTypeKey` and `HttpRequestMessageOptions.RestMethodInfoKey` to respectively access the interface type and REST method info from the options.

#### MethodInfo of the method on the Refit client interface that was invoked

There may be times when you want access to the `MethodInfo` of the method on the Refit client interface that was invoked. An example is where you
want to decorate the method with a custom attribute in order to control some aspect of behavior in a `DelegatingHandler`:

```csharp
public interface ISomeAPI
{
[SomeCustomAttribute("SomeValue")]
[Get("/{id}")]
Task<ApiResponse<SomeClass>> GetById(int id);
}
```
To make the `MethodInfo` available you need to opt-in via the `RefitSettings` like so:

```csharp
services.AddRefitClient<ISomeAPI>(provider => new RefitSettings
{
InjectMethodInfoAsProperty = true
})
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));
```

You can access the `MethodInfo` for use in a handler and then get the custom attributes:

```csharp
class RequestPropertyHandler : DelegatingHandler
{
public RequestPropertyHandler(HttpMessageHandler innerHandler = null) : base(innerHandler ?? new HttpClientHandler()) {}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Get the MethodInfo
MethodInfo methodInfo;
request.Options.TryGetValue(HttpRequestMessageOptions.MethodInfoKey, out methodInfo);

//get the custom attributes
var customAttributes = methodInfo.CustomAttributes;

//insert your logic here
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
```

Note: for .NET Core 3.1 and lower this will be available via `HttpRequestMessage.Properties[HttpRequestMessageOptions.MethodInfo]`.

### Multipart uploads

Methods decorated with `Multipart` attribute will be submitted with multipart content type.
Expand Down
4 changes: 2 additions & 2 deletions Refit.Tests/MultipartTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,12 @@ public async Task MultipartUploadShouldWorkWithHeaderAndRequestProperty()
Assert.Equal(someHeader, message.Headers.Authorization.ToString());

#if NET6_0_OR_GREATER
Assert.Equal(2, message.Options.Count());
Assert.Equal(3, message.Options.Count());
Assert.Equal(someProperty, ((IDictionary<string, object>)message.Options)["SomeProperty"]);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.Equal(2, message.Properties.Count);
Assert.Equal(3, message.Properties.Count);
Assert.Equal(someProperty, message.Properties["SomeProperty"]);
#pragma warning restore CS0618 // Type or member is obsolete
},
Expand Down
81 changes: 11 additions & 70 deletions Refit.Tests/RequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,6 @@ public class ComplexQueryObject

public class RestMethodInfoTests
{

[Fact]
public void TooManyComplexTypesThrows()
{
Expand Down Expand Up @@ -514,7 +513,7 @@ public void ParameterMappingSmokeTest()
Assert.Empty(fixture.QueryParameterMap);
Assert.Null(fixture.BodyParameterInfo);
}

[Fact]
public void ParameterMappingWithTheSameIdInAFewPlaces()
{
Expand Down Expand Up @@ -2175,12 +2174,6 @@ public void InterfaceTypeShouldBeInProperties()
var factory = fixture.BuildRequestFactoryForMethod(nameof(IContainAandB.Ping));
var output = factory(Array.Empty<object>());

#if NET6_0_OR_GREATER
Assert.NotEmpty(output.Options);
output.Options.TryGetValue(HttpRequestMessageOptions.InterfaceTypeKey, out var interfaceType);
Assert.Equal(typeof(IContainAandB), interfaceType);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.NotEmpty(output.Properties);
Assert.Equal(typeof(IContainAandB), output.Properties[HttpRequestMessageOptions.InterfaceType]);
Expand All @@ -2189,75 +2182,23 @@ public void InterfaceTypeShouldBeInProperties()
}

[Fact]
public void MethodInfoShouldBeInPropertiesIfInjectMethodInfoAsPropertyTrue()
{
var fixture = new RequestBuilderImplementation<IContainAandB>(new RefitSettings
{
InjectMethodInfoAsProperty = true
});
var factory = fixture.BuildRequestFactoryForMethod(nameof(IContainAandB.Ping));
var output = factory(Array.Empty<object>());

RestMethodInfo restMethodInfo;
#if NET6_0_OR_GREATER
Assert.NotEmpty(output.Options);
output.Options.TryGetValue(HttpRequestMessageOptions.RestMethodInfoKey, out restMethodInfo);
Assert.NotNull(restMethodInfo);
Assert.Equal(nameof(IContainAandB.Ping), restMethodInfo.Name);
Assert.Equal(typeof(IAmInterfaceA), restMethodInfo.MethodInfo.DeclaringType);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.NotEmpty(output.Properties);
restMethodInfo = (RestMethodInfo)(output.Properties[HttpRequestMessageOptions.RestMethodInfo]);
Assert.NotNull(restMethodInfo);
Assert.Equal(nameof(IContainAandB.Ping), restMethodInfo.Name);
Assert.Equal(typeof(IAmInterfaceA), restMethodInfo.MethodInfo.DeclaringType);
#pragma warning restore CS0618 // Type or member is obsolete
}

[Fact]
public void MethodInfoShouldNotBeInPropertiesIfInjectMethodInfoAsPropertyFalse()
public void RestMethodInfoShouldBeInProperties()
{
var someProperty = new object();
var fixture = new RequestBuilderImplementation<IContainAandB>();
var factory = fixture.BuildRequestFactoryForMethod(nameof(IContainAandB.Ping));
var output = factory(Array.Empty<object>());
var output = factory(new object[] { });

RestMethodInfo restMethodInfo;
#if NET6_0_OR_GREATER
Assert.NotEmpty(output.Options);
output.Options.TryGetValue(HttpRequestMessageOptions.RestMethodInfoKey, out restMethodInfo);
Assert.Null(restMethodInfo);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.True(output.Options.TryGetValue(new HttpRequestOptionsKey<RestMethodInfo>(HttpRequestMessageOptions.RestMethodInfo), out var restMethodInfo));
#else
Assert.NotEmpty(output.Properties);
Assert.False(output.Properties.ContainsKey(HttpRequestMessageOptions.RestMethodInfo));
#pragma warning restore CS0618 // Type or member is obsolete
}

[Fact]
public void RestMethodInfoShouldBeInProperties()
{
var fixture = new RequestBuilderImplementation<IContainAandB>(new() { InjectMethodInfoAsProperty = true });
var factory = fixture.BuildRequestFactoryForMethod(nameof(IContainAandB.Ping));
var output = factory(Array.Empty<object>());
RestMethodInfo restMethodInfo;
#if NET6_0_OR_GREATER
Assert.NotEmpty(output.Options);
output.Options.TryGetValue(HttpRequestMessageOptions.RestMethodInfoKey, out restMethodInfo);
Assert.NotNull(restMethodInfo);
Assert.Equal(nameof(IContainAandB.Ping), restMethodInfo.Name);
Assert.Equal(typeof(IAmInterfaceA), restMethodInfo.MethodInfo.DeclaringType);
Assert.True(output.Properties.TryGetValue(HttpRequestMessageOptions.RestMethodInfo, out var restMethodInfoObj));
Assert.IsType<RestMethodInfo>(restMethodInfoObj);
var restMethodInfo = restMethodInfoObj as RestMethodInfo;
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.NotEmpty(output.Properties);
restMethodInfo = (RestMethodInfo)(output.Properties[HttpRequestMessageOptions.RestMethodInfo]);
Assert.NotNull(restMethodInfo);
Assert.Equal(nameof(IContainAandB.Ping), restMethodInfo.Name);
Assert.Equal(typeof(IAmInterfaceA), restMethodInfo.MethodInfo.DeclaringType);
#pragma warning restore CS0618 // Type or member is obsolete
}

[Fact]
Expand Down Expand Up @@ -2293,12 +2234,12 @@ public void DynamicRequestPropertiesWithDuplicateKeyShouldOverwritePreviousPrope


#if NET6_0_OR_GREATER
Assert.Equal(2, output.Options.Count());
Assert.Equal(3, output.Options.Count());
Assert.Equal(someOtherProperty, ((IDictionary<string, object>)output.Options)["SomeProperty"]);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.Equal(2, output.Properties.Count);
Assert.Equal(3, output.Properties.Count);
Assert.Equal(someOtherProperty, output.Properties["SomeProperty"]);
#pragma warning restore CS0618 // Type or member is obsolete
}
Expand Down
19 changes: 1 addition & 18 deletions Refit/HttpRequestMessageProperties.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Reflection;

namespace Refit
{
/// <summary>
Expand All @@ -12,24 +10,9 @@ public static class HttpRequestMessageOptions
/// </summary>
public static string InterfaceType { get; } = "Refit.InterfaceType";

#if NET6_0_OR_GREATER
/// <summary>
/// A typed key to access the <see cref="System.Type"/> of the top-level interface where the method was called from
/// on the <see cref="System.Net.Http.HttpRequestMessage.Options"/>.
/// </summary>
public static System.Net.Http.HttpRequestOptionsKey<System.Type> InterfaceTypeKey { get; } = new(InterfaceType);
#endif

/// <summary>
/// Returns the <see cref="Refit.RestMethodInfo"/> of the method that was called
/// Returns the <see cref="Refit.RestMethodInfo"/> of the top-level interface
/// </summary>
public static string RestMethodInfo { get; } = "Refit.RestMethodInfo";

#if NET6_0_OR_GREATER
/// <summary>
/// A typed key to access the <see cref="Refit.RestMethodInfo"/> of the method that was called
/// </summary>
public static System.Net.Http.HttpRequestOptionsKey<RestMethodInfo> RestMethodInfoKey { get; } = new(RestMethodInfo);
#endif
}
}
27 changes: 1 addition & 26 deletions Refit/RefitSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,21 @@ public RefitSettings()
ExceptionFactory = new DefaultApiExceptionFactory(this).CreateAsync;
}


#if NET6_0_OR_GREATER
/// <summary>
/// Creates a new <see cref="RefitSettings"/> instance with the specified parameters
/// </summary>
/// <param name="contentSerializer">The <see cref="IHttpContentSerializer"/> instance to use</param>
/// <param name="urlParameterFormatter">The <see cref="IUrlParameterFormatter"/> instance to use (defaults to <see cref="DefaultUrlParameterFormatter"/>)</param>
/// <param name="formUrlEncodedParameterFormatter">The <see cref="IFormUrlEncodedParameterFormatter"/> instance to use (defaults to <see cref="DefaultFormUrlEncodedParameterFormatter"/>)</param>
/// <param name="injectMethodInfoAsProperty">Controls injecting the <see cref="MethodInfo"/> of the method on the Refit client interface that was invoked into the HttpRequestMessage.Options (defaults to false)</param>
#else
/// <summary>
/// Creates a new <see cref="RefitSettings"/> instance with the specified parameters
/// </summary>
/// <param name="contentSerializer">The <see cref="IHttpContentSerializer"/> instance to use</param>
/// <param name="urlParameterFormatter">The <see cref="IUrlParameterFormatter"/> instance to use (defaults to <see cref="DefaultUrlParameterFormatter"/>)</param>
/// <param name="formUrlEncodedParameterFormatter">The <see cref="IFormUrlEncodedParameterFormatter"/> instance to use (defaults to <see cref="DefaultFormUrlEncodedParameterFormatter"/>)</param>
/// <param name="injectMethodInfoAsProperty">Controls injecting the <see cref="MethodInfo"/> of the method on the Refit client interface that was invoked into the HttpRequestMessage.Properties (defaults to false)</param>
#endif
public RefitSettings(
IHttpContentSerializer contentSerializer,
IUrlParameterFormatter? urlParameterFormatter = null,
IFormUrlEncodedParameterFormatter? formUrlEncodedParameterFormatter = null,
bool injectMethodInfoAsProperty = false)
IFormUrlEncodedParameterFormatter? formUrlEncodedParameterFormatter = null)
{
ContentSerializer = contentSerializer ?? throw new ArgumentNullException(nameof(contentSerializer), "The content serializer can't be null");
UrlParameterFormatter = urlParameterFormatter ?? new DefaultUrlParameterFormatter();
FormUrlEncodedParameterFormatter = formUrlEncodedParameterFormatter ?? new DefaultFormUrlEncodedParameterFormatter();
ExceptionFactory = new DefaultApiExceptionFactory(this).CreateAsync;
InjectMethodInfoAsProperty = injectMethodInfoAsProperty;
}

/// <summary>
Expand Down Expand Up @@ -103,17 +89,6 @@ public RefitSettings(
/// Optional Key-Value pairs, which are displayed in the property <see cref="HttpRequestMessage.Options"/> or <see cref="HttpRequestMessage.Properties"/>.
/// </summary>
public Dictionary<string, object> HttpRequestMessageOptions { get; set; }

#if NET6_0_OR_GREATER
/// <summary>
/// Controls injecting the <see cref="MethodInfo"/> of the method on the Refit client interface that was invoked into the HttpRequestMessage.Options (defaults to false)
/// </summary>
#else
/// <summary>
/// Controls injecting the <see cref="MethodInfo"/> of the method on the Refit client interface that was invoked into the HttpRequestMessage.Properties (defaults to false)
/// </summary>
#endif
public bool InjectMethodInfoAsProperty { get; set; }
}

/// <summary>
Expand Down
17 changes: 5 additions & 12 deletions Refit/RequestBuilderImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -746,21 +746,14 @@ Func<object[], HttpRequestMessage> BuildRequestFactoryForMethod(RestMethodInfo r
#endif
}

// Always add the top-level type of the interface to the options/properties and include the MethodInfo if the developer has opted-in to that behavior
// Always add the top-level type of the interface to the properties
#if NET6_0_OR_GREATER
ret.Options.Set(HttpRequestMessageOptions.InterfaceTypeKey, TargetType);
if (settings.InjectMethodInfoAsProperty)
{
ret.Options.Set(HttpRequestMessageOptions.RestMethodInfoKey, restMethod);
}
ret.Options.Set(new HttpRequestOptionsKey<Type>(HttpRequestMessageOptions.InterfaceType), TargetType);
ret.Options.Set(new HttpRequestOptionsKey<RestMethodInfo>(HttpRequestMessageOptions.RestMethodInfo), restMethod);
#else
ret.Properties[HttpRequestMessageOptions.InterfaceType] = TargetType;
if (settings.InjectMethodInfoAsProperty)
{
ret.Properties[HttpRequestMessageOptions.RestMethodInfo] = restMethod;
}

#endif
ret.Properties[HttpRequestMessageOptions.RestMethodInfo] = restMethod;
#endif

// NB: The URI methods in .NET are dumb. Also, we do this
// UriBuilder business so that we preserve any hardcoded query
Expand Down

0 comments on commit 2a79e02

Please sign in to comment.