8000 Add interface type to HttpMessageRequest.Options by clairernovotny · Pull Request #1041 · reactiveui/refit · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add interface type to HttpMessageRequest.Options #1041

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

Merged
merged 4 commits into from
Jan 27, 2021
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
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ services.AddRefitClient<IGitHubApi>("https://api.github.com");
* [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](#target-interface-type)
* [Multipart uploads](#multipart-uploads)
* [Retrieving the response](#retrieving-the-response)
* [Using generic interfaces](#using-generic-interfaces)
Expand Down Expand Up @@ -778,6 +779,54 @@ Because Refit supports `HttpClientFactory` it is possible to configure Polly pol
If your policy makes use of `Polly.Context` this can be passed via Refit by adding `[Property("PollyExecutionContext")] Polly.Context context`
as behind the scenes `Polly.Context` is simply stored in `HttpRequestMessage.Properties` under the key `PollyExecutionContext` and is of type `Polly.Context`

#### Target Interface Type

There may be times when you want to know what the target interface type is of the Refit instance. An example is where you
have a derived interface that implements a common base like this:

```csharp
public interface IGetAPI<TEntity>
{
[Get("/{key}")]
Task<TEntity> Get(long key);
}

public interface IUsersAPI : IGetAPI<User>
{
}

public interface IOrdersAPI : IGetAPI<Order>
{
}
```

You can access the concrete type of the interface for use in a handler, such as to alter the URL of the request:

[//]: # ({% raw %})
```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 type of the target interface
Type interfaceType = (Type)request.Properties[HttpMessageRequestOptions.InterfaceType];

var builder = new UriBuilder(request.RequestUri);
// Alter the Path in some way based on the interface or an attribute on it
builder.Path = $"/{interfaceType.Name}{builder.Path}";
// Set the new Uri on the outgoing message
request.RequestUri = builder.Uri;

return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
```
[//]: # ({% endraw %})

Note: in .NET 5 `HttpRequestMessage.Properties` has been marked `Obsolete` and Refit will instead populate the value into the new `HttpRequestMessage.Options`.

### Multipart uploads

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

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

#pragma warning disable CS0618 // Type or member is obsolete
Assert.Single(message.Properties);
Assert.Equal(2, message.Properties.Count);
Assert.Equal(someProperty, message.Properties["SomeProperty"]);
#pragma warning restore CS0618 // Type or member is obsolete
},
Expand Down
30 changes: 25 additions & 5 deletions Refit.Tests/RequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1802,7 +1802,7 @@ public void DynamicRequestPropertiesShouldBeInProperties()
var factory = fixture.BuildRequestFactoryForMethod(nameof(IDummyHttpApi.FetchSomeStuffWithDynamicRequestProperty));
var output = factory(new object[] { 6, someProperty });

#if NET5_0
#if NET5_0_OR_GREATER
Assert.NotEmpty(output.Options);
Assert.Equal(someProperty, ((IDictionary<string, object>)output.Options)["SomeProperty"]);
#endif
Expand All @@ -1814,6 +1814,26 @@ public void DynamicRequestPropertiesShouldBeInProperties()

}

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

#if NET5_0_OR_GREATER
Assert.NotEmpty(output.Options);
Assert.Equal(typeof(IContainAandB), ((IDictionary<string, object>)output.Options)[HttpRequestMessageOptions.InterfaceType]);
#endif

#pragma warning disable CS0618 // Type or member is obsolete
Assert.NotEmpty(output.Properties);
Assert.Equal(typeof(IContainAandB), output.Properties[HttpRequestMessageOptions.InterfaceType]);
#pragma warning restore CS0618 // Type or member is obsolete

}

[Fact]
public void DynamicRequestPropertiesWithDefaultKeysShouldBeInProperties()
{
Expand All @@ -1823,7 +1843,7 @@ public void DynamicRequestPropertiesWithDefaultKeysShouldBeInProperties()
var factory = fixture.BuildRequestFactoryForMethod(nameof(IDummyHttpApi.FetchSomeStuffWithDynamicRequestPropertyWithoutKey));
var output = factory(new object[] { 6, someProperty, someOtherProperty });

#if NET5_0
#if NET5_0_OR_GREATER
Assert.NotEmpty(output.Options);
Assert.Equal(someProperty, ((IDictionary<string, object>)output.Options)["someValue"]);
Assert.Equal(someOtherProperty, ((IDictionary<string, object>)output.Options)["someOtherValue"]);
Expand All @@ -1846,13 +1866,13 @@ public void DynamicRequestPropertiesWithDuplicateKeyShouldOverwritePreviousPrope
var output = factory(new object[] { 6, someProperty, someOtherProperty });


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

#pragma warning disable CS0618 // Type or member is obsolete
Assert.Single(output.Properties);
Assert.Equal(2, output.Properties.Count);
Assert.Equal(someOtherProperty, output.Properties["SomeProperty"]);
#pragma warning restore CS0618 // Type or member is obsolete
}
Expand Down
2 changes: 1 addition & 1 deletion Refit/Buffers/PooledBufferWriter.Stream.NETStandard21.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if NETSTANDARD2_1 || NET5_0
#if NETSTANDARD2_1 || NET5_0_OR_GREATER

using System;
using System.IO;
Expand Down
2 changes: 1 addition & 1 deletion Refit/Buffers/PooledBufferWriter.Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio

try
{
#if NETSTANDARD2_1 || NET5_0
#if NETSTANDARD2_1 || NET5_0_OR_GREATER
return CopyToInternalAsync(destination, cancellationToken);
#else
CopyTo(destination, bufferSize);
Expand Down
2 changes: 1 addition & 1 deletion Refit/HttpContentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Refit
{
#if !NET5_0
#if !NET5_0_OR_GREATER
static class HttpContentExtensions
{
#pragma warning disable IDE0079 // Remove unnecessary suppression
Expand Down
19 changes: 19 additions & 0 deletions Refit/HttpRequestMessageProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Refit
{
/// <summary>
/// Contains Refit-defined properties on the HttpRequestMessage.Properties/Options
/// </summary>
public static class HttpRequestMessageOptions
{
/// <summary>
/// Returns the <see cref="System.Type"/> of the top-level interface where the method was called from
/// </summary>
public static string InterfaceType { get; } = "Refit.InterfaceType";
}
}
11 changes: 10 additions & 1 deletion Refit/RequestBuilderImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -712,13 +712,22 @@ Func<object[], Task<HttpRequestMessage>> BuildRequestFactoryForMethod(RestMethod

foreach (var property in propertiesToAdd)
{
#if NET5_0
#if NET5_0_OR_GREATER
ret.Options.Set(new HttpRequestOptionsKey<object?>(property.Key), property.Value);
#else
ret.Properties[property.Key] = property.Value;
#endif
}

// Always add the top-level type of the interface to the properties
#if NET5_0_OR_GREATER
ret.Options.Set(new HttpRequestOptionsKey<Type>(HttpRequestMessageOptions.InterfaceType), TargetType);
#else
ret.Properties[HttpRequestMessageOptions.InterfaceType] = TargetType;
#endif

;

// NB: The URI methods in .NET are dumb. Also, we do this
// UriBuilder business so that we preserve any hardcoded query
// parameters as well as add the parameterized ones.
Expand Down
2 changes: 1 addition & 1 deletion Refit/SystemTextJsonContentSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public Task<HttpContent> SerializeAsync<T>(T item)

try
{
#if NET5_0
#if NET5_0_OR_GREATER
var utf8Length = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
#else
var utf8Length = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
Expand Down
0