diff --git a/RestSharp.sln b/RestSharp.sln index b3efff12e..2902ccf04 100644 --- a/RestSharp.sln +++ b/RestSharp.sln @@ -35,7 +35,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestSharp.Tests.Serializers EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceGen", "SourceGen", "{55B8F371-B2BA-4DEE-AB98-5BAB8A21B1C2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenerator", "gen\SourceGenerator\SourceGenerator.csproj", "{FE778406-ADCF-45A1-B775-A054B55BFC50}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGenerator", "gen\SourceGenerator\SourceGenerator.csproj", "{FE778406-ADCF-45A1-B775-A054B55BFC50}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -446,6 +446,36 @@ Global {FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x64.Build.0 = Release|Any CPU {FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x86.ActiveCfg = Release|Any CPU {FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x86.Build.0 = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|ARM.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|ARM.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|Mixed Platforms.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|x64.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|x64.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|x86.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug.Appveyor|x86.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|ARM.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|ARM.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|x64.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|x64.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|x86.ActiveCfg = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Debug|x86.Build.0 = Debug|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|Any CPU.Build.0 = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|ARM.ActiveCfg = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|ARM.Build.0 = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|x64.ActiveCfg = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|x64.Build.0 = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|x86.ActiveCfg = Release|Any CPU + {5E8D472F-5A12-4CD8-8DBE-E3F6E0F76798}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/gen/SourceGenerator/SourceGenerator.csproj b/gen/SourceGenerator/SourceGenerator.csproj index 9e63fcfeb..623b9e879 100644 --- a/gen/SourceGenerator/SourceGenerator.csproj +++ b/gen/SourceGenerator/SourceGenerator.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 diff --git a/src/RestSharp/Interceptors/Interceptor.cs b/src/RestSharp/Interceptors/Interceptor.cs new file mode 100644 index 000000000..2d0c016be --- /dev/null +++ b/src/RestSharp/Interceptors/Interceptor.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation and Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace RestSharp.Interceptors; + +/// +/// Base Interceptor +/// +public abstract class Interceptor { + /// + /// Intercepts the request before serialization + /// + /// RestRequest before serialization + /// Value Tags + public virtual ValueTask InterceptBeforeSerialization(RestRequest request) { + return new(); + } + + /// + /// Intercepts the request before being sent + /// + /// HttpRequestMessage before being sent + /// Value Tags + public virtual ValueTask InterceptBeforeRequest(HttpRequestMessage req) { + return new(); + } + + /// + /// Intercepts the request before being sent + /// + /// HttpResponseMessage as received from Server + /// Value Tags + public virtual ValueTask InterceptAfterRequest(HttpResponseMessage responseMessage) { + return new(); + } + + /// + /// Intercepts the request before deserialization + /// + /// HttpResponseMessage as received from Server + /// Value Tags + public virtual ValueTask InterceptBeforeDeserialize(RestResponse response) { + return new(); + } +} \ No newline at end of file diff --git a/src/RestSharp/Options/RestClientOptions.cs b/src/RestSharp/Options/RestClientOptions.cs index 49c197ffc..95db15020 100644 --- a/src/RestSharp/Options/RestClientOptions.cs +++ b/src/RestSharp/Options/RestClientOptions.cs @@ -22,6 +22,7 @@ using System.Text; using RestSharp.Authenticators; using RestSharp.Extensions; +using RestSharp.Interceptors; // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable PropertyCanBeMadeInitOnly.Global @@ -64,6 +65,8 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba /// public IAuthenticator? Authenticator { get; set; } + public List Interceptors { get; set; } = new(); + /// /// Passed to Credentials property /// diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs index d3701e221..6fd390810 100644 --- a/src/RestSharp/RestClient.Async.cs +++ b/src/RestSharp/RestClient.Async.cs @@ -76,7 +76,7 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo if (_disposed) { throw new ObjectDisposedException(nameof(RestClient)); } - + await OnBeforeSerialization(request); request.ValidateParameters(); var authenticator = request.Authenticator ?? Options.Authenticator; if (authenticator != null) await authenticator.Authenticate(this, request).ConfigureAwait(false); @@ -107,8 +107,8 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo .AddCookieHeaders(url, Options.CookieContainer); message.AddHeaders(headers); - if (request.OnBeforeRequest != null) await request.OnBeforeRequest(message).ConfigureAwait(false); + await OnBeforeRequest(message); var responseMessage = await HttpClient.SendAsync(message, request.CompletionOption, ct).ConfigureAwait(false); @@ -121,6 +121,7 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo } if (request.OnAfterRequest != null) await request.OnAfterRequest(responseMessage).ConfigureAwait(false); + await OnAfterRequest(responseMessage); return new HttpResponse(responseMessage, url, cookieContainer, null, timeoutCts.Token); } @@ -129,6 +130,34 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo } } + /// + /// Will be called before the Request becomes Serialized + /// + /// RestRequest before it will be serialized + async Task OnBeforeSerialization(RestRequest request) { + foreach (var interceptor in Options.Interceptors) { + await interceptor.InterceptBeforeSerialization(request); //.ThrowExceptionIfAvailable(); + } + } + /// + /// Will be called before the Request will be sent + /// + /// HttpRequestMessage ready to be sent + async Task OnBeforeRequest(HttpRequestMessage requestMessage) { + foreach (var interceptor in Options.Interceptors) { + await interceptor.InterceptBeforeRequest(requestMessage); + } + } + /// + /// Will be called after the Response has been received from Server + /// + /// HttpResponseMessage as received from server + async Task OnAfterRequest(HttpResponseMessage responseMessage) { + foreach (var interceptor in Options.Interceptors) { + await interceptor.InterceptAfterRequest(responseMessage); + } + } + record HttpResponse( HttpResponseMessage? ResponseMessage, Uri Url, diff --git a/src/RestSharp/RestSharp.csproj b/src/RestSharp/RestSharp.csproj index 1d9e0d915..c8b3cbcec 100644 --- a/src/RestSharp/RestSharp.csproj +++ b/src/RestSharp/RestSharp.csproj @@ -39,4 +39,7 @@ + + + diff --git a/src/RestSharp/Serializers/RestSerializers.cs b/src/RestSharp/Serializers/RestSerializers.cs index 00bb8de91..425cdbd62 100644 --- a/src/RestSharp/Serializers/RestSerializers.cs +++ b/src/RestSharp/Serializers/RestSerializers.cs @@ -39,6 +39,7 @@ internal RestResponse Deserialize(RestRequest request, RestResponse raw, R try { request.OnBeforeDeserialization?.Invoke(raw); + OnBeforeDeserialization(raw, options).ConfigureAwait(true); response.Data = DeserializeContent(raw); } catch (Exception ex) { @@ -53,6 +54,16 @@ internal RestResponse Deserialize(RestRequest request, RestResponse raw, R return response; } + /// + /// Will be called before the Data will be serialized + /// + /// RestResponse with Data still in Content + /// RestClient options but readonly + async Task OnBeforeDeserialization(RestResponse raw, ReadOnlyRestClientOptions options) { + foreach (var interceptor in options.Interceptors) { + await interceptor.InterceptBeforeDeserialize(raw); //.ThrowExceptionIfAvailable(); + } + } /// /// Deserialize the response content into the specified type diff --git a/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj b/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj index c25f980cf..5cbf895be 100644 --- a/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj +++ b/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj @@ -8,4 +8,7 @@ + + + diff --git a/test/RestSharp.Tests.Serializers.Xml/SampleClasses/twitter.cs b/test/RestSharp.Tests.Serializers.Xml/SampleClasses/twitter.cs index df64ad212..bf0d94e63 100644 --- a/test/RestSharp.Tests.Serializers.Xml/SampleClasses/twitter.cs +++ b/test/RestSharp.Tests.Serializers.Xml/SampleClasses/twitter.cs @@ -23,7 +23,7 @@ public class status { public string in_reply_to_screen_name { get; set; } // ignore contributors for now - public user user { get; set; } + public User user { get; set; } // ignore geo public long id { get; set; } @@ -31,7 +31,7 @@ public class status { public string text { get; set; } } -public class user { +public class User { public string url { get; set; } public string description { get; set; } @@ -105,7 +105,7 @@ public class complexStatus { public string in_reply_to_screen_name { get; set; } // ignore contributors for now - [DeserializeAs(Name = "user.following")] + [DeserializeAs(Name = "User.following")] public bool follow { get; set; } // ignore geo