Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert #1418, #1367, introduce public version of RestMethodInfo #1496

Merged
merged 8 commits into from
May 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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