Skip to content
Open
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
1 change: 1 addition & 0 deletions Refit.HttpClientFactory/Refit.HttpClientFactory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Description>Refit HTTP Client Factory Extensions</Description>
<TargetFrameworks>$(RefitTargets)</TargetFrameworks>
<Nullable>enable</Nullable>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions Refit.Newtonsoft.Json/Refit.Newtonsoft.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<RootNamespace>Refit</RootNamespace>
<Nullable>enable</Nullable>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Refit.HttpClientFactory")]
[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Refit.HttpClientFactory")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Refit.Newtonsoft.Json")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Refit.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Refit.Xml")]
Expand Down Expand Up @@ -367,10 +368,14 @@ namespace Refit
}
public static class RequestBuilder
{
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Refit uses reflection to analyze interface methods. Ensure referenced interfaces " +
"and DTOs are preserved when trimming.")]
public static Refit.IRequestBuilder ForType(System.Type refitInterfaceType) { }
public static Refit.IRequestBuilder ForType(System.Type refitInterfaceType, Refit.RefitSettings? settings) { }
public static Refit.IRequestBuilder<T> ForType<T>() { }
public static Refit.IRequestBuilder<T> ForType<T>(Refit.RefitSettings? settings) { }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Refit uses reflection to analyze interface methods. Ensure referenced interfaces " +
"and DTOs are preserved when trimming.")]
public static Refit.IRequestBuilder ForType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.None | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods)] System.Type refitInterfaceType, Refit.RefitSettings? settings) { }
public static Refit.IRequestBuilder<T> ForType<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.None | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods)] T>() { }
public static Refit.IRequestBuilder<T> ForType<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.None | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Refit.RefitSettings? settings) { }
}
public class RestMethodInfo : System.IEquatable<Refit.RestMethodInfo>
{
Expand Down
2 changes: 1 addition & 1 deletion Refit.Tests/Refit.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Deterministic>false</Deterministic>
<!-- Some tests rely on CallerFilePath -->
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json</RestoreAdditionalProjectSources>
<NoWarn>$(NoWarn);CS1591;CA1819;CA2000;CA2007;CA1056;CA1707;CA1861;xUnit1031</NoWarn>
<NoWarn>$(NoWarn);CS1591;CA1819;CA2000;CA2007;CA1056;CA1707;CA1861;CA1515;xUnit1031</NoWarn>
<MockHttpVersion>6.0.0</MockHttpVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions Refit.Xml/Refit.Xml.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<RootNamespace>Refit</RootNamespace>
<Nullable>enable</Nullable>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
Expand Down
14 changes: 14 additions & 0 deletions Refit/AotCompatibility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
#if NET7_0_OR_GREATER
using System.Runtime.InteropServices;
#endif

namespace Refit
{
internal static class AotCompatibility
{
// Intentionally left blank to avoid changing public API surface (e.g., assembly-level attributes)
// while keeping a central place for any future AOT-related initializers if needed.
}
}
19 changes: 19 additions & 0 deletions Refit/Polyfills.Trimming.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#if NETSTANDARD2_0 || NET462
namespace System.Diagnostics.CodeAnalysis
{
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Constructor | System.AttributeTargets.Method | System.AttributeTargets.Property | System.AttributeTargets.Event, Inherited = false)]
internal sealed class RequiresUnreferencedCodeAttribute : System.Attribute
{
public RequiresUnreferencedCodeAttribute(string message) => Message = message;
public string Message { get; }
public string? Url { get; set; }
}

[System.AttributeUsage(System.AttributeTargets.Constructor | System.AttributeTargets.Method, AllowMultiple = true)]
internal sealed class DynamicDependencyAttribute : System.Attribute
{
public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, System.Type type) { }

Check warning on line 15 in Refit/Polyfills.Trimming.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Add a public read-only property accessor for positional argument memberTypes of Attribute DynamicDependencyAttribute (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1019)
public DynamicDependencyAttribute(string memberSignature, System.Type type) { }
}
}
#endif
16 changes: 14 additions & 2 deletions Refit/Refit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,23 @@
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('net8.0')) or $(TargetFramework.StartsWith('net9.0')) or $(TargetFramework.StartsWith('net10.0'))">
<IsAotCompatible>true</IsAotCompatible>
<PublishAotSupported>true</PublishAotSupported>
<TrimMode>link</TrimMode>
<IsTrimmable>true</IsTrimmable>
<IlcGenerateCompleteTypeMetadata>true</IlcGenerateCompleteTypeMetadata>
</PropertyGroup>

<!-- Ensure DisableRuntimeMarshalling is only emitted for .NET 10, not .NET 8/9 -->
<PropertyGroup Condition="$(TargetFramework.StartsWith('net8.0')) or $(TargetFramework.StartsWith('net9.0'))">
<DisableRuntimeMarshalling>false</DisableRuntimeMarshalling>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('net10.0'))">
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'net462'">
<PackageReference Include="System.Text.Json" Version="9.0.3" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
<PackageReference Include="System.Text.Json" Version="9.0.9" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.9" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
Expand Down
90 changes: 48 additions & 42 deletions Refit/RequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,71 +1,77 @@
using System.Net.Http;
#if NET10_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif

namespace Refit
{
/// <summary>
/// IRequestBuilder.
/// </summary>
public interface IRequestBuilder

Check warning on line 8 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'IRequestBuilder'

Check warning on line 8 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder'

Check warning on line 8 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder'
{
/// <summary>
/// Builds the rest result function for method.
/// </summary>
/// <param name="methodName">Name of the method.</param>
/// <param name="parameterTypes">The parameter types.</param>
/// <param name="genericArgumentTypes">The generic argument types.</param>
/// <returns></returns>
Func<HttpClient, object[], object?> BuildRestResultFuncForMethod(

Check warning on line 10 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'IRequestBuilder.BuildRestResultFuncForMethod(string, Type[]?, Type[]?)'

Check warning on line 10 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder.BuildRestResultFuncForMethod(string, Type[]?, Type[]?)'

Check warning on line 10 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder.BuildRestResultFuncForMethod(string, Type[]?, Type[]?)'
string methodName,
Type[]? parameterTypes = null,
Type[]? genericArgumentTypes = null
);
}

/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IRequestBuilder<T> : IRequestBuilder { }

Check warning on line 17 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'IRequestBuilder<T>'

Check warning on line 17 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder<T>'

Check warning on line 17 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'IRequestBuilder<T>'

/// <summary>
/// RequestBuilder.
/// </summary>
public static class RequestBuilder

Check warning on line 19 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'RequestBuilder'

Check warning on line 19 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder'

Check warning on line 19 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder'
{
static readonly RequestBuilderFactory PlatformRequestBuilderFactory = new();

/// <summary>
/// Fors the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="settings">The settings.</param>
/// <returns></returns>
#if NET10_0_OR_GREATER
public static IRequestBuilder<T> ForType< [
DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.None |
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods
)] T >(RefitSettings? settings) =>
PlatformRequestBuilderFactory.Create<T>(settings);
#else
public static IRequestBuilder<T> ForType<T>(RefitSettings? settings) =>

Check warning on line 32 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>(RefitSettings?)'

Check warning on line 32 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>(RefitSettings?)'

Check warning on line 32 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>(RefitSettings?)'
PlatformRequestBuilderFactory.Create<T>(settings);
#endif

/// <summary>
/// Fors the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
#if NET10_0_OR_GREATER
public static IRequestBuilder<T> ForType< [
DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.None |
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods
)] T >() =>
PlatformRequestBuilderFactory.Create<T>(null);
#else
public static IRequestBuilder<T> ForType<T>() =>

Check warning on line 45 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>()'

Check warning on line 45 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>()'

Check warning on line 45 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType<T>()'
PlatformRequestBuilderFactory.Create<T>(null);
#endif

/// <summary>
/// Fors the type.
/// </summary>
/// <param name="refitInterfaceType">Type of the refit interface.</param>
/// <param name="settings">The settings.</param>
/// <returns></returns>
public static IRequestBuilder ForType(Type refitInterfaceType, RefitSettings? settings) =>
PlatformRequestBuilderFactory.Create(refitInterfaceType, settings);
#if NET10_0_OR_GREATER
[RequiresUnreferencedCode("Refit uses reflection to analyze interface methods. Ensure referenced interfaces and DTOs are preserved when trimming.")]
public static IRequestBuilder ForType(
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.None |
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods
)] Type refitInterfaceType,
RefitSettings? settings
)
#else
public static IRequestBuilder ForType(

Check warning on line 60 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type, RefitSettings?)'

Check warning on line 60 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type, RefitSettings?)'

Check warning on line 60 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type, RefitSettings?)'
Type refitInterfaceType,
RefitSettings? settings
)
#endif
{
return new CachedRequestBuilderImplementation(
new RequestBuilderImplementation(refitInterfaceType, settings)
);
}

/// <summary>
/// Fors the type.
/// </summary>
/// <param name="refitInterfaceType">Type of the refit interface.</param>
/// <returns></returns>
#if NET10_0_OR_GREATER
[RequiresUnreferencedCode("Refit uses reflection to analyze interface methods. Ensure referenced interfaces and DTOs are preserved when trimming.")]
#endif
public static IRequestBuilder ForType(Type refitInterfaceType) =>

Check warning on line 74 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-windows

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type)'

Check warning on line 74 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (macos-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type)'

Check warning on line 74 in Refit/RequestBuilder.cs

View workflow job for this annotation

GitHub Actions / build / build-unix (ubuntu-latest)

Missing XML comment for publicly visible type or member 'RequestBuilder.ForType(Type)'
PlatformRequestBuilderFactory.Create(refitInterfaceType, null);
ForType(refitInterfaceType, null);
}
}
42 changes: 35 additions & 7 deletions Refit/RequestBuilderImplementation.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Web;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif

namespace Refit
{
class RequestBuilderImplementation<TApi>(RefitSettings? refitSettings = null)
: RequestBuilderImplementation(typeof(TApi), refitSettings),
IRequestBuilder<TApi> { }
#if NET5_0_OR_GREATER
class RequestBuilderImplementation<
[
DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods
)] TApi> : RequestBuilderImplementation, IRequestBuilder<TApi>
#else
class RequestBuilderImplementation<TApi> : RequestBuilderImplementation, IRequestBuilder<TApi>
#endif
{
public RequestBuilderImplementation(RefitSettings? refitSettings = null)
: base(typeof(TApi), refitSettings)
{
}
}

partial class RequestBuilderImplementation : IRequestBuilder
{
Expand All @@ -27,7 +41,15 @@ readonly ConcurrentDictionary<
readonly RefitSettings settings;
public Type TargetType { get; }

#if NET5_0_OR_GREATER
[RequiresUnreferencedCode("RequestBuilder uses reflection on the provided Refit interface and DTO types. Ensure necessary members are preserved when trimming.")]
#endif
public RequestBuilderImplementation(
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods
)]
#endif
Type refitInterfaceType,
RefitSettings? refitSettings = null
)
Expand Down Expand Up @@ -65,7 +87,10 @@ static string GetLookupKeyForMethod(MethodInfo methodInfo)
}

void AddInterfaceHttpMethods(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]Type interfaceType,
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
#endif
Type interfaceType,
Dictionary<string, List<RestMethodInfoInternal>> methods
)
{
Expand Down Expand Up @@ -175,6 +200,9 @@ RestMethodInfoInternal CloseGenericMethodIfNeeded(
return restMethodInfo;
}

#if NET5_0_OR_GREATER
[RequiresUnreferencedCode("Building rest result delegates uses reflection over interface methods and DTOs. Ensure required members are preserved.")]
#endif
public Func<HttpClient, object[], object?> BuildRestResultFuncForMethod(
string methodName,
Type[]? parameterTypes = null,
Expand Down Expand Up @@ -1197,12 +1225,12 @@ var value in ParseEnumerableQueryParameterValue(
CollectionFormat.Ssv => " ",
CollectionFormat.Tsv => "\t",
CollectionFormat.Pipes => "|",
_ => ","
_ => ","
};

// Missing a "default" clause was preventing the collection from serializing at all, as it was hitting "continue" thus causing an off-by-one error
var formattedValues = paramValues
.Cast<object>()
.Cast<object> ()
.Select(
v =>
settings.UrlParameterFormatter.Format(
Expand Down
Loading