diff --git a/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj b/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj index 63f716d41..a6f6558f2 100644 --- a/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj +++ b/Refit.HttpClientFactory/Refit.HttpClientFactory.csproj @@ -5,6 +5,7 @@ Refit HTTP Client Factory Extensions $(RefitTargets) enable + true diff --git a/Refit.Newtonsoft.Json/Refit.Newtonsoft.Json.csproj b/Refit.Newtonsoft.Json/Refit.Newtonsoft.Json.csproj index e30550800..2d984f915 100644 --- a/Refit.Newtonsoft.Json/Refit.Newtonsoft.Json.csproj +++ b/Refit.Newtonsoft.Json/Refit.Newtonsoft.Json.csproj @@ -7,6 +7,7 @@ true Refit enable + true diff --git a/Refit.Tests/API/_snapshots/ApiApprovalTests.Refit.DotNet10_0.verified.txt b/Refit.Tests/API/_snapshots/ApiApprovalTests.Refit.DotNet10_0.verified.txt index bbb331d37..da6125aa0 100644 --- a/Refit.Tests/API/_snapshots/ApiApprovalTests.Refit.DotNet10_0.verified.txt +++ b/Refit.Tests/API/_snapshots/ApiApprovalTests.Refit.DotNet10_0.verified.txt @@ -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")] @@ -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 ForType() { } - public static Refit.IRequestBuilder ForType(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 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 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 { diff --git a/Refit.Tests/Refit.Tests.csproj b/Refit.Tests/Refit.Tests.csproj index 198617bdf..2b87861f8 100644 --- a/Refit.Tests/Refit.Tests.csproj +++ b/Refit.Tests/Refit.Tests.csproj @@ -7,7 +7,7 @@ false https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json - $(NoWarn);CS1591;CA1819;CA2000;CA2007;CA1056;CA1707;CA1861;xUnit1031 + $(NoWarn);CS1591;CA1819;CA2000;CA2007;CA1056;CA1707;CA1861;CA1515;xUnit1031 6.0.0 false diff --git a/Refit.Xml/Refit.Xml.csproj b/Refit.Xml/Refit.Xml.csproj index e7d37c92e..7a26c5df1 100644 --- a/Refit.Xml/Refit.Xml.csproj +++ b/Refit.Xml/Refit.Xml.csproj @@ -7,6 +7,7 @@ true Refit enable + true diff --git a/Refit/AotCompatibility.cs b/Refit/AotCompatibility.cs new file mode 100644 index 000000000..bdb57d679 --- /dev/null +++ b/Refit/AotCompatibility.cs @@ -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. + } +} diff --git a/Refit/Polyfills.Trimming.cs b/Refit/Polyfills.Trimming.cs new file mode 100644 index 000000000..600852f9b --- /dev/null +++ b/Refit/Polyfills.Trimming.cs @@ -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) { } + public DynamicDependencyAttribute(string memberSignature, System.Type type) { } + } +} +#endif diff --git a/Refit/Refit.csproj b/Refit/Refit.csproj index ede96070f..cb328d0e3 100644 --- a/Refit/Refit.csproj +++ b/Refit/Refit.csproj @@ -9,11 +9,23 @@ true + true + link + true + true + + + + + false + + + true - - + + diff --git a/Refit/RequestBuilder.cs b/Refit/RequestBuilder.cs index aa06da38b..0eb6ca62b 100644 --- a/Refit/RequestBuilder.cs +++ b/Refit/RequestBuilder.cs @@ -1,19 +1,12 @@ using System.Net.Http; +#if NET10_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace Refit { - /// - /// IRequestBuilder. - /// public interface IRequestBuilder { - /// - /// Builds the rest result function for method. - /// - /// Name of the method. - /// The parameter types. - /// The generic argument types. - /// Func BuildRestResultFuncForMethod( string methodName, Type[]? parameterTypes = null, @@ -21,51 +14,64 @@ public interface IRequestBuilder ); } - /// - /// - /// - /// public interface IRequestBuilder : IRequestBuilder { } - /// - /// RequestBuilder. - /// public static class RequestBuilder { static readonly RequestBuilderFactory PlatformRequestBuilderFactory = new(); - /// - /// Fors the type. - /// - /// - /// The settings. - /// +#if NET10_0_OR_GREATER + public static IRequestBuilder ForType< [ + DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.None | + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.NonPublicMethods + )] T >(RefitSettings? settings) => + PlatformRequestBuilderFactory.Create(settings); +#else public static IRequestBuilder ForType(RefitSettings? settings) => PlatformRequestBuilderFactory.Create(settings); +#endif - /// - /// Fors the type. - /// - /// - /// +#if NET10_0_OR_GREATER + public static IRequestBuilder ForType< [ + DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.None | + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.NonPublicMethods + )] T >() => + PlatformRequestBuilderFactory.Create(null); +#else public static IRequestBuilder ForType() => PlatformRequestBuilderFactory.Create(null); +#endif - /// - /// Fors the type. - /// - /// Type of the refit interface. - /// The settings. - /// - 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( + Type refitInterfaceType, + RefitSettings? settings + ) +#endif + { + return new CachedRequestBuilderImplementation( + new RequestBuilderImplementation(refitInterfaceType, settings) + ); + } - /// - /// Fors the type. - /// - /// Type of the refit interface. - /// +#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) => - PlatformRequestBuilderFactory.Create(refitInterfaceType, null); + ForType(refitInterfaceType, null); } } diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index f7fddd4d4..b520ff7d2 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -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(RefitSettings? refitSettings = null) - : RequestBuilderImplementation(typeof(TApi), refitSettings), - IRequestBuilder { } +#if NET5_0_OR_GREATER + class RequestBuilderImplementation< + [ + DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods + )] TApi> : RequestBuilderImplementation, IRequestBuilder +#else + class RequestBuilderImplementation : RequestBuilderImplementation, IRequestBuilder +#endif + { + public RequestBuilderImplementation(RefitSettings? refitSettings = null) + : base(typeof(TApi), refitSettings) + { + } + } partial class RequestBuilderImplementation : IRequestBuilder { @@ -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 ) @@ -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> methods ) { @@ -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 BuildRestResultFuncForMethod( string methodName, Type[]? parameterTypes = null, @@ -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() + .Cast () .Select( v => settings.UrlParameterFormatter.Format(