Skip to content

Commit

Permalink
Revert #1418, #1367, introduce public version of RestMethodInfo (#1496)
Browse files Browse the repository at this point in the history
* Revert "feature: Add named httpclient support (#1418)"

This reverts commit b78bbc7.

* Revert "Feature: allow developers to inject the MethodInfo as a Property (#1367)"

This reverts commit b06ef7c.

* no

* Make RestMethodInfo internal

* Implement a public RestMethodInfo

* Reenable RestMethodInfo, but our public one instead

amend

* Allow configuring the HttpClient name

* Fix typo
  • Loading branch information
anaisbetts authored May 14, 2023
1 parent ef46395 commit e4a3565
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 302 deletions.
3 changes: 1 addition & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<DefaultLanguage>en-US</DefaultLanguage>
<Description>The automatic type-safe REST library for Xamarin and .NET</Description>
<NoPackageAnalysis>true</NoPackageAnalysis>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Refit.ruleset</CodeAnalysisRuleSet>
<LangVersion>preview</LangVersion>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Net Analyzers config taken from : https://docs.microsoft.com/en-gb/visualstudio/code-quality/migrate-from-fxcop-analyzers-to-net-analyzers?view=vs-2019 -->
Expand Down Expand Up @@ -53,4 +52,4 @@
</AssemblyAttribute>
</ItemGroup>
</Target>
</Project>
</Project>
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
32 changes: 14 additions & 18 deletions Refit.HttpClientFactory/HttpClientFactoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;

using Refit.HttpClientFactory;

namespace Refit
{
public static class HttpClientFactoryExtensions
Expand Down Expand Up @@ -42,25 +40,18 @@ public static IHttpClientBuilder AddRefitClient(this IServiceCollection services
/// <typeparam name="T">Type of the Refit interface</typeparam>
/// <param name="services">container</param>
/// <param name="settingsAction">Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.</param>
/// <param name="httpClientName">Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.</param>
/// <returns></returns>
public static IHttpClientBuilder AddRefitClient<T>(this IServiceCollection services, Func<IServiceProvider, RefitSettings?>? settingsAction) where T : class => AddRefitClient<T>(services, null, settingsAction);

/// <summary>
/// Adds a Refit client to the DI container
/// </summary>
/// <typeparam name="T">Type of the Refit interface</typeparam>
/// <param name="services">container</param>
/// <param name="name">Named http client</param>
/// <param name="settingsAction">Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.</param>
/// <returns></returns>
public static IHttpClientBuilder AddRefitClient<T>(this IServiceCollection services, string? name, Func<IServiceProvider, RefitSettings?>? settingsAction) where T : class
public static IHttpClientBuilder AddRefitClient<T>(
this IServiceCollection services,
Func<IServiceProvider, RefitSettings?>? settingsAction,
string? httpClientName = null) where T : class
{
services.AddSingleton(provider => new SettingsFor<T>(settingsAction?.Invoke(provider)));
services.AddSingleton(provider => RequestBuilder.ForType<T>(provider.GetRequiredService<SettingsFor<T>>().Settings));
services.AddScoped<IRefitHttpClientFactory, RefitHttpClientFactory>();

return services
.AddHttpClient(name ?? UniqueName.ForType<T>())
.AddHttpClient(httpClientName ?? UniqueName.ForType<T>())
.ConfigureHttpMessageHandlerBuilder(builder =>
{
// check to see if user provided custom auth token
Expand All @@ -69,7 +60,7 @@ public static IHttpClientBuilder AddRefitClient<T>(this IServiceCollection servi
builder.PrimaryHandler = innerHandler;
}
})
.AddTypedClient((client, serviceProvider) => RestService.For(client, serviceProvider.GetService<IRequestBuilder<T>>()!));
.AddTypedClient((client, serviceProvider) => RestService.For<T>(client, serviceProvider.GetService<IRequestBuilder<T>>()!));
}

/// <summary>
Expand All @@ -78,16 +69,21 @@ public static IHttpClientBuilder AddRefitClient<T>(this IServiceCollection servi
/// <param name="services">container</param>
/// <param name="refitInterfaceType">Type of the Refit interface</param>
/// <param name="settingsAction">Optional. Action to configure refit settings. This method is called once and only once, avoid using any scoped dependencies that maybe be disposed automatically.</param>
/// <param name="httpClientName">Optional. Allows users to change the HttpClient name as provided to IServiceCollection.AddHttpClient. Useful for logging scenarios.</param>
/// <returns></returns>
public static IHttpClientBuilder AddRefitClient(this IServiceCollection services, Type refitInterfaceType, Func<IServiceProvider, RefitSettings?>? settingsAction)
public static IHttpClientBuilder AddRefitClient(
this IServiceCollection services,
Type refitInterfaceType,
Func<IServiceProvider, RefitSettings?>? settingsAction,
string? httpClientName = null)
{
var settingsType = typeof(SettingsFor<>).MakeGenericType(refitInterfaceType);
var requestBuilderType = typeof(IRequestBuilder<>).MakeGenericType(refitInterfaceType);
services.AddSingleton(settingsType, provider => Activator.CreateInstance(typeof(SettingsFor<>).MakeGenericType(refitInterfaceType)!, settingsAction?.Invoke(provider))!);
services.AddSingleton(requestBuilderType, provider => RequestBuilderGenericForTypeMethod.MakeGenericMethod(refitInterfaceType).Invoke(null, new object?[] { ((ISettingsFor)provider.GetRequiredService(settingsType)).Settings })!);

return services
.AddHttpClient(UniqueName.ForType(refitInterfaceType))
.AddHttpClient(httpClientName ?? UniqueName.ForType(refitInterfaceType))
.ConfigureHttpMessageHandlerBuilder(builder =>
{
// check to see if user provided custom auth token
Expand Down
6 changes: 0 additions & 6 deletions Refit.HttpClientFactory/IRefitHttpClientFactory.cs

This file was deleted.

25 changes: 0 additions & 25 deletions Refit.HttpClientFactory/RefitHttpClientFactory.cs

This file was deleted.

6 changes: 3 additions & 3 deletions Refit.Tests/MultipartTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public async Task MultipartUploadShouldWorkWithStreamAndCustomBoundary()
}

var input = typeof(IRunscopeApi);
var methodFixture = new RestMethodInfo(input, input.GetMethods().First(x => x.Name == "UploadStreamWithCustomBoundary"));
var methodFixture = new RestMethodInfoInternal(input, input.GetMethods().First(x => x.Name == "UploadStreamWithCustomBoundary"));
Assert.Equal("-----SomeCustomBoundary", methodFixture.MultipartBoundary);
}

Expand Down 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
Loading

0 comments on commit e4a3565

Please sign in to comment.