Skip to content

Commit 74970c2

Browse files
committed
- Added compatibility interceptor
- Added docs - Refactored tests for the hooks - Updated packages
1 parent 78d7d0b commit 74970c2

File tree

18 files changed

+266
-60
lines changed

18 files changed

+266
-60
lines changed

Directory.Packages.props

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,43 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<PropertyGroup Label="Package versions for .NET 6" Condition="$(TargetFramework) == 'net6.0'">
6-
<MicrosoftTestHostVer>[6.0.22,7)</MicrosoftTestHostVer>
6+
<MicrosoftTestHostVer>[6.0.28,7)</MicrosoftTestHostVer>
77
</PropertyGroup>
88
<PropertyGroup Label="Package versions for .NET 7" Condition="$(TargetFramework) == 'net7.0'">
9-
<MicrosoftTestHostVer>7.0.11</MicrosoftTestHostVer>
9+
<MicrosoftTestHostVer>7.0.17</MicrosoftTestHostVer>
1010
</PropertyGroup>
1111
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
12-
<MicrosoftTestHostVer>8.0.0-rc.1.23421.29</MicrosoftTestHostVer>
12+
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
1313
</PropertyGroup>
14-
<ItemGroup>
15-
<PackageVersion Include="HttpTracer" Version="2.1.1"/>
16-
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)"/>
17-
<PackageVersion Include="Polly" Version="7.2.4"/>
18-
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0"/>
19-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
20-
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.0" PrivateAssets="All"/>
21-
<PackageVersion Include="coverlet.collector" Version="6.0.0"/>
22-
<PackageVersion Include="xunit" Version="2.5.0"/>
23-
<PackageVersion Include="AutoFixture" Version="4.18.0"/>
24-
<PackageVersion Include="FluentAssertions" Version="6.12.0"/>
25-
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3"/>
26-
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1"/>
27-
<PackageVersion Include="MinVer" Version="4.3.0"/>
28-
<PackageVersion Include="JetBrains.Annotations" Version="2023.2.0"/>
14+
<ItemGroup Label="Runtime dependencies">
2915
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
3016
<PackageVersion Include="CsvHelper" Version="30.0.1"/>
31-
<PackageVersion Include="Nullable" Version="1.3.1"/>
32-
<PackageVersion Include="System.Text.Json" Version="7.0.3"/>
33-
<PackageVersion Include="BenchmarkDotNet" Version="0.13.8"/>
34-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0"/>
17+
<PackageVersion Include="System.Text.Json" Version="8.0.3"/>
18+
</ItemGroup>
19+
<ItemGroup Label="Compile dependencies">
20+
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12"/>
21+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
3522
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"/>
23+
<PackageVersion Include="MinVer" Version="5.0.0"/>
24+
<PackageVersion Include="Nullable" Version="1.3.1"/>
25+
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3"/>
26+
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
27+
<PackageVersion Include="JetBrains.Annotations" Version="2023.2.0"/>
28+
</ItemGroup>
29+
<ItemGroup Label="Testing dependencies">
30+
<PackageVersion Include="AutoFixture" Version="4.18.0"/>
31+
<PackageVersion Include="coverlet.collector" Version="6.0.2"/>
32+
<PackageVersion Include="FluentAssertions" Version="6.12.0"/>
33+
<PackageVersion Include="HttpTracer" Version="2.1.1"/>
34+
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)"/>
35+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0"/>
3636
<PackageVersion Include="Moq" Version="4.20.69"/>
37-
<PackageVersion Include="RichardSzalay.MockHttp" Version="6.0.0"/>
38-
<PackageVersion Include="System.Net.Http.Json" Version="7.0.1"/>
37+
<PackageVersion Include="Polly" Version="8.3.1"/>
3938
<PackageVersion Include="rest-mock-core" Version="0.7.12"/>
39+
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0"/>
40+
<PackageVersion Include="System.Net.Http.Json" Version="8.0.0"/>
41+
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0"/>
42+
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" PrivateAssets="All"/>
43+
<PackageVersion Include="xunit" Version="2.7.0"/>
4044
</ItemGroup>
4145
</Project>

docs/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = {
2727
"usage.md",
2828
"serialization.md",
2929
"authenticators.md",
30+
"interceptors.md",
3031
"error-handling.md"
3132
]
3233
}

docs/interceptors.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: Interceptors
3+
---
4+
5+
## Intercepting requests and responses
6+
7+
Interceptors are a powerful feature of RestSharp that allows you to modify requests and responses before they are sent or received. You can use interceptors to add headers, modify the request body, or even cancel the request. You can also use interceptors to modify the response before it is returned to the caller.
8+
9+
### Implementing an interceptor
10+
11+
To implement an interceptor, you need to create a class that inherits the `Interceptor` base class. The base class implements all interceptor methods as virtual, so you can override them in your derived class.
12+
13+
Methods that you can override are:
14+
- `BeforeRequest(RestRequest request, CancellationToken cancellationToken)`
15+
- `AfterRequest(RestResponse response, CancellationToken cancellationToken)`
16+
- `BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken)`
17+
- `AfterHttpResponse(HttpResponseMessage responseMessage, CancellationToken cancellationToken)`
18+
- `BeforeDeserialization(RestResponse response, CancellationToken cancellationToken)`
19+
20+
All those functions must return a `ValueTask` instance.
21+
22+
Here's an example of an interceptor that adds a header to a request:
23+
24+
```csharp
25+
// This interceptor adds a header to the request
26+
// You'd not normally use this interceptor, as RestSharp already has a method
27+
// to add headers to the request
28+
class HeaderInterceptor(string headerName, string headerValue) : Interceptors.Interceptor {
29+
public override ValueTask BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken) {
30+
requestMessage.Headers.Add(headerName, headerValue);
31+
return ValueTask.CompletedTask;
32+
}
33+
}
34+
```
35+
36+
Because interceptor functions return `ValueTask`, you can use `async` and `await` inside them.
37+
38+
### Using an interceptor
39+
40+
It's possible to add as many interceptors as you want, both to the client and to the request. The interceptors are executed in the order they were added.
41+
42+
Adding interceptors to the client is done via the client options:
43+
44+
```csharp
45+
var options = new RestClientOptions("https://api.example.com") {
46+
Interceptors = [new HeaderInterceptor("Authorization", token)]
47+
};
48+
var client = new RestClient(options);
49+
```
50+
51+
When you add an interceptor to the client, it will be executed for every request made by that client.
52+
53+
You can also add an interceptor to a specific request:
54+
55+
```csharp
56+
var request = new RestRequest("resource") {
57+
Interceptors = [new HeaderInterceptor("Authorization", token)]
58+
};
59+
```
60+
61+
In this case, the interceptor will only be executed for that specific request.
62+
63+
### Deprecation notice
64+
65+
Interceptors aim to replace the existing request hooks available in RestSharp prior to version 111.0. Those hooks are marked with `Obsolete` attribute and will be removed in the future. If you are using those hooks, we recommend migrating to interceptors as soon as possible.
66+
67+
To make the migration easier, RestSharp provides a class called `CompatibilityInterceptor`. It has properties for the hooks available in RestSharp 110.0 and earlier. You can use it to migrate your code to interceptors without changing the existing logic.
68+
69+
For example, a code that uses `OnBeforeRequest` hook:
70+
71+
```csharp
72+
var request = new RestRequest("success");
73+
request.OnBeforeDeserialization += _ => throw new Exception(exceptionMessage);
74+
```
75+
76+
Can be migrated to interceptors like this:
77+
78+
```csharp
79+
var request = new RestRequest("success") {
80+
Interceptors = [new CompatibilityInterceptor {
81+
OnBeforeDeserialization = _ => throw new Exception(exceptionMessage)
82+
}]
83+
};
84+
```

src/Directory.Build.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
<PackageReadmeFile>README.md</PackageReadmeFile>
1919
</PropertyGroup>
2020

21+
<PropertyGroup Condition="'$(TargetFramework)' == 'net472' Or '$(TargetFramework)' == 'netstandard2.0'">
22+
<AddSystemTextJson>true</AddSystemTextJson>
23+
<AddNullable>true</AddNullable>
24+
</PropertyGroup>
2125
<ItemGroup>
2226
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All"/>
2327
<PackageReference Include="MinVer" PrivateAssets="All"/>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,24 @@
11
# About
22

3+
The `RestSharp.Serializers.CsvHelper` library provides a CSV serializer for RestSharp. It is based on the
4+
`CsvHelper` library.
5+
6+
# How to use
7+
8+
Use the extension method provided by the package to configure the client:
9+
10+
```csharp
11+
var client = new RestClient(
12+
options,
13+
configureSerialization: s => s.UseCsvHelper()
14+
);
15+
```
16+
17+
You can also supply your instance of `CsvConfiguration` as a parameter for the extension method.
18+
19+
```csharp
20+
var client = new RestClient(
21+
options,
22+
configureSerialization: s => s.UseCsvHelper(new CsvConfiguration(CultureInfo.InvariantCulture) {...})
23+
);
24+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) .NET Foundation and Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace RestSharp.Interceptors;
16+
17+
/// <summary>
18+
/// This class allows easier migration of legacy request hooks to interceptors.
19+
/// </summary>
20+
public class CompatibilityInterceptor : Interceptor {
21+
public Action<RestResponse>? OnBeforeDeserialization { get; set; }
22+
public Func<HttpRequestMessage, ValueTask>? OnBeforeRequest { get; set; }
23+
public Func<HttpResponseMessage, ValueTask>? OnAfterRequest { get; set; }
24+
25+
/// <inheritdoc />
26+
public override ValueTask BeforeDeserialization(RestResponse response, CancellationToken cancellationToken) {
27+
OnBeforeDeserialization?.Invoke(response);
28+
return default;
29+
}
30+
31+
public override ValueTask BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken) {
32+
OnBeforeRequest?.Invoke(requestMessage);
33+
return default;
34+
}
35+
36+
public override ValueTask AfterHttpRequest(HttpResponseMessage responseMessage, CancellationToken cancellationToken) {
37+
OnAfterRequest?.Invoke(responseMessage);
38+
return default;
39+
}
40+
}

src/RestSharp/Parameters/FileParameter.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ public static FileParameter FromFile(
113113

114114
[PublicAPI]
115115
public class FileParameterOptions {
116-
[Obsolete("Use DisableFilenameStar instead")]
117-
public bool DisableFileNameStar {
118-
get => DisableFilenameStar;
119-
set => DisableFilenameStar = value;
120-
}
121116
public bool DisableFilenameStar { get; set; } = true;
122117
public bool DisableFilenameEncoding { get; set; }
123118
}

src/RestSharp/Request/RequestContent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ StreamContent ToStreamContent(FileParameter fileParameter) {
8383

8484
HttpContent Serialize(BodyParameter body) {
8585
return body.DataFormat switch {
86-
DataFormat.None => new StringContent(body.Value!.ToString(), client.Options.Encoding, body.ContentType.Value),
86+
DataFormat.None => new StringContent(body.Value!.ToString()!, client.Options.Encoding, body.ContentType.Value),
8787
DataFormat.Binary => GetBinary(),
8888
_ => GetSerialized()
8989
};
@@ -171,7 +171,7 @@ void AddPostParameters(GetOrPostParameter[] postParameters) {
171171
}
172172
}
173173
else {
174-
var encodedItems = postParameters.Select(x => $"{x.Name!.UrlEncode()}={x.Value?.ToString().UrlEncode() ?? string.Empty}");
174+
var encodedItems = postParameters.Select(x => $"{x.Name!.UrlEncode()}={x.Value?.ToString()?.UrlEncode() ?? string.Empty}");
175175
var encodedContent = new StringContent(encodedItems.JoinToString("&"), client.Options.Encoding, ContentType.FormUrlEncoded.Value);
176176

177177
if (client.Options.DisableCharset) {

src/RestSharp/Request/RestRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public RestRequest(Uri resource, Method method = Method.Get)
185185
[Obsolete("Use Interceptors instead")]
186186
public Func<HttpResponseMessage, ValueTask>? OnAfterRequest { get; set; }
187187

188-
internal void IncreaseNumAttempts() => Attempts++;
188+
internal void IncreaseNumberOfAttempts() => Attempts++;
189189

190190
/// <summary>
191191
/// How many attempts were made to send this Request

src/RestSharp/Response/RestResponseBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public abstract class RestResponseBase {
2828
protected RestResponseBase(RestRequest request) {
2929
ResponseStatus = ResponseStatus.None;
3030
Request = request;
31-
Request.IncreaseNumAttempts();
31+
Request.IncreaseNumberOfAttempts();
3232
}
3333

3434
/// <summary>

0 commit comments

Comments
 (0)