Skip to content
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

Wrapping up interceptors, fixing typos, removing Moq #2141

Merged
merged 25 commits into from Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d81d8ac
Wrapping up interceptors, fixing typos, removing Moq
alexeyzimarev Sep 14, 2023
b2500c2
Add Linux runner, and publish tests results from Linux
alexeyzimarev Sep 15, 2023
8fd9767
A bit of cleanup and adding package versions for source gen
alexeyzimarev Sep 15, 2023
121bf93
Add missing version
alexeyzimarev Sep 15, 2023
4a40bcd
Fixed centralized package versions
alexeyzimarev Sep 18, 2023
2bda7ee
Leave exception handling as it is
alexeyzimarev Oct 24, 2023
b582f59
Remove duplicate package version
alexeyzimarev Oct 24, 2023
95ed004
Cleanup
alexeyzimarev Apr 2, 2024
f6be96c
Merge branch 'refs/heads/dev' into wrapping-up-interceptors
alexeyzimarev Apr 2, 2024
78d7d0b
Fix the readme issue
alexeyzimarev Apr 2, 2024
74970c2
- Added compatibility interceptor
alexeyzimarev Apr 2, 2024
7d8f957
Add permissions to publish test results
alexeyzimarev Apr 2, 2024
56a1161
Refactoring tests to WireMock
alexeyzimarev Apr 3, 2024
3165eaf
Allow body for GET and HEAD in tests
alexeyzimarev Apr 3, 2024
3e09a51
Cleaning up fixtures
alexeyzimarev Apr 3, 2024
f7f689e
Fix the download test
alexeyzimarev Apr 3, 2024
1b11eb2
Disable NTML tests on Linux
alexeyzimarev Apr 3, 2024
8446276
Add matrix
alexeyzimarev Apr 3, 2024
8657a8b
Try running 472 too
alexeyzimarev Apr 3, 2024
251aee8
Refactoring tests
alexeyzimarev Apr 5, 2024
aac4921
Add .NET Framework 4.8 target
alexeyzimarev Apr 5, 2024
229550a
Add 4.8 to the matrix
alexeyzimarev Apr 5, 2024
51abdef
Fixing compilation
alexeyzimarev Apr 5, 2024
ce369ef
Content type fix
alexeyzimarev Apr 5, 2024
b80b25f
Disable non-ASCII upload test for .NET 6
alexeyzimarev Apr 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/ISSUE_TEMPLATE/bug_report.md
Expand Up @@ -9,6 +9,13 @@ assignees: ''

**DO NOT USE ISSUES FOR QUESTIONS**

Note: New issues raised, where it is clear the submitter has not read the issue template,
are likely to be closed with `invalid` label. Please understand that this is not meant to be rude,
but to keep the issue list clean and useful for everyone. If one opening the issue decides to ignore the issue template,
the maintainers might also decide to ignore the issue.

**Remove** all the text above the next line when submitting your issue.

**Describe the bug**
A clear and concise description of what the bug is.
Hint: use a tool like https://requestbin.com to compare working and non-working requests.
Expand All @@ -22,14 +29,13 @@ A clear and concise description of what you expected to happen.
Post the working request here as well if you made it work using Postman, Swagger, or any other client.
You can use https://requestbin.com/r to create a public request bin and share the link in the issue.


**Stack trace**
Copy the full stack trace here if you get an exception.

**Desktop (please complete the following information):**
- OS: [e.g. macOS]
- .NET version [e.g. .NET 5]
- Version [e.g. 107.0.4]
- .NET version [e.g. .NET 6]
- Version [e.g. 110.2.0]

**Additional context**
Add any other context about the problem here.
68 changes: 60 additions & 8 deletions .github/workflows/pull-request.yml
Expand Up @@ -4,24 +4,76 @@ on: [pull_request]

permissions:
contents: read
checks: write

jobs:
test:
event_file:
name: "Event File"
runs-on: ubuntu-latest
steps:
- name: Upload
uses: actions/upload-artifact@v4
with:
name: Event File
path: ${{ github.event_path }}
test-windows:
runs-on: windows-latest
strategy:
matrix:
dotnet: ['net48', 'net6.0', 'net7.0', 'net8.0']

steps:
-
name: Checkout
uses: actions/checkout@v4
# -
# name: Setup .NET
# uses: actions/setup-dotnet@v3
# with:
# dotnet-version: '8.0'
-
name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
-
name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: Test Results Windows ${{ matrix.dotnet }}
path: |
test-results/**/*.xml
test-results/**/*.trx
test-results/**/*.json

test-linux:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: ['net6.0', 'net7.0', 'net8.0']

steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0'
# -
# name: Setup .NET
# uses: actions/setup-dotnet@v3
# with:
# dotnet-version: '8.0'
-
name: Run tests
run: dotnet test -c Release

run: dotnet test -f ${{ matrix.dotnet }}
-
name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: Test Results Ubuntu ${{ matrix.dotnet }}
path: |
test-results/**/*.xml
test-results/**/*.trx
test-results/**/*.json

docs:
runs-on: ubuntu-latest

Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/test-results.yml
@@ -0,0 +1,37 @@
name: Test Results

on:
workflow_run:
workflows: ["Build and test PRs"]
types:
- completed
permissions: {}

jobs:
test-results:
name: Test Results
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion != 'skipped'

permissions:
checks: write
pull-requests: write
actions: read

steps:
-
name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
-
name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
commit: ${{ github.event.workflow_run.head_sha }}
event_file: artifacts/Event File/event.json
event_name: ${{ github.event.workflow_run.event }}
files: |
artifacts/**/*.xml
artifacts/**/*.trx
49 changes: 49 additions & 0 deletions Directory.Packages.props
@@ -0,0 +1,49 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 6" Condition="$(TargetFramework) == 'net6.0'">
<MicrosoftTestHostVer>[6.0.28,7)</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 7" Condition="$(TargetFramework) == 'net7.0'">
<MicrosoftTestHostVer>7.0.17</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
</PropertyGroup>
<ItemGroup Label="Runtime dependencies">
<PackageVersion Include="HttpMultipartParser" Version="8.3.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="CsvHelper" Version="30.0.1" />
<PackageVersion Include="PolySharp" Version="1.14.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.3" />
<PackageVersion Include="WireMock.Net" Version="1.5.51" />
<PackageVersion Include="WireMock.Net.FluentAssertions" Version="1.5.51" />
</ItemGroup>
<ItemGroup Label="Compile dependencies">
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="MinVer" Version="5.0.0" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2023.2.0" />
</ItemGroup>
<ItemGroup Label="Testing dependencies">
<PackageVersion Include="AutoFixture" Version="4.18.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="HttpTracer" Version="2.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Moq" Version="4.20.69" />
<PackageVersion Include="Polly" Version="8.3.1" />
<PackageVersion Include="rest-mock-core" Version="0.7.12" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageVersion Include="System.Net.Http.Json" Version="8.0.0" />
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" PrivateAssets="All" />
<PackageVersion Include="xunit" Version="2.7.0" />
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions RestSharp.sln.DotSettings
Expand Up @@ -67,6 +67,9 @@
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/PreferQualifiedReference/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FDIC/@EntryIndexedValue">FDIC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -77,6 +80,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=advisor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Advisors/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
Expand Down
21 changes: 11 additions & 10 deletions benchmarks/RestSharp.Benchmarks/RestSharp.Benchmarks.csproj
@@ -1,24 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>10</LangVersion>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<RepoRoot>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'RestSharp.sln'))</RepoRoot>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
<PackageReference Include="AutoFixture"/>
<PackageReference Include="BenchmarkDotNet"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\RestSharp.Serializers.NewtonsoftJson\RestSharp.Serializers.NewtonsoftJson.csproj" />
<ProjectReference Include="..\..\test\RestSharp.Tests.Shared\RestSharp.Tests.Shared.csproj" />
<ProjectReference Include="$(RepoRoot)\src\RestSharp.Serializers.NewtonsoftJson\RestSharp.Serializers.NewtonsoftJson.csproj"/>
<ProjectReference Include="$(RepoRoot)\test\RestSharp.Tests.Shared\RestSharp.Tests.Shared.csproj"/>
</ItemGroup>
<ItemGroup>
<Compile Remove="BenchmarkDotNet.Artifacts\**" />
<EmbeddedResource Remove="BenchmarkDotNet.Artifacts\**" />
<None Remove="BenchmarkDotNet.Artifacts\**" />
<Compile Remove="BenchmarkDotNet.Artifacts\**"/>
<EmbeddedResource Remove="BenchmarkDotNet.Artifacts\**"/>
<None Remove="BenchmarkDotNet.Artifacts\**"/>
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Expand Up @@ -27,6 +27,7 @@ module.exports = {
"usage.md",
"serialization.md",
"authenticators.md",
"interceptors.md",
"error-handling.md"
]
}
Expand Down
84 changes: 84 additions & 0 deletions docs/interceptors.md
@@ -0,0 +1,84 @@
---
title: Interceptors
---

## Intercepting requests and responses

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.

### Implementing an interceptor

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.

Methods that you can override are:
- `BeforeRequest(RestRequest request, CancellationToken cancellationToken)`
- `AfterRequest(RestResponse response, CancellationToken cancellationToken)`
- `BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken)`
- `AfterHttpResponse(HttpResponseMessage responseMessage, CancellationToken cancellationToken)`
- `BeforeDeserialization(RestResponse response, CancellationToken cancellationToken)`

All those functions must return a `ValueTask` instance.

Here's an example of an interceptor that adds a header to a request:

```csharp
// This interceptor adds a header to the request
// You'd not normally use this interceptor, as RestSharp already has a method
// to add headers to the request
class HeaderInterceptor(string headerName, string headerValue) : Interceptors.Interceptor {
public override ValueTask BeforeHttpRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken) {
requestMessage.Headers.Add(headerName, headerValue);
return ValueTask.CompletedTask;
}
}
```

Because interceptor functions return `ValueTask`, you can use `async` and `await` inside them.

### Using an interceptor

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.

Adding interceptors to the client is done via the client options:

```csharp
var options = new RestClientOptions("https://api.example.com") {
Interceptors = [new HeaderInterceptor("Authorization", token)]
};
var client = new RestClient(options);
```

When you add an interceptor to the client, it will be executed for every request made by that client.

You can also add an interceptor to a specific request:

```csharp
var request = new RestRequest("resource") {
Interceptors = [new HeaderInterceptor("Authorization", token)]
};
```

In this case, the interceptor will only be executed for that specific request.

### Deprecation notice

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.

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.

For example, a code that uses `OnBeforeRequest` hook:

```csharp
var request = new RestRequest("success");
request.OnBeforeDeserialization += _ => throw new Exception(exceptionMessage);
```

Can be migrated to interceptors like this:

```csharp
var request = new RestRequest("success") {
Interceptors = [new CompatibilityInterceptor {
OnBeforeDeserialization = _ => throw new Exception(exceptionMessage)
}]
};
```
28 changes: 27 additions & 1 deletion docs/serialization.md
Expand Up @@ -37,7 +37,10 @@ In previous versions of RestSharp, the default XML serializer was a custom RestS
You can add it back if necessary by installing the package and adding it to the client:

```csharp
client.UseXmlSerializer();
var client = new RestClient(
options,
configureSerialization: s => s.UseXmlSerializer()
);
```

As before, you can supply three optional arguments for a custom namespace, custom root element, and if you want to use `SerializeAs` and `DeserializeAs` attributed.
Expand Down Expand Up @@ -77,6 +80,29 @@ JsonSerializerSettings DefaultSettings = new JsonSerializerSettings {
If you need to use different settings, you can supply your instance of
`JsonSerializerSettings` as a parameter for the extension method.

## CSV

A separate package `RestSharp.Serializers.CsvHelper` provides a CSV serializer for RestSharp. It is based on the
`CsvHelper` library.

Use the extension method provided by the package to configure the client:

```csharp
var client = new RestClient(
options,
configureSerialization: s => s.UseCsvHelper()
);
```

You can also supply your instance of `CsvConfiguration` as a parameter for the extension method.

```csharp
var client = new RestClient(
options,
configureSerialization: s => s.UseCsvHelper(new CsvConfiguration(CultureInfo.InvariantCulture) {...})
);
```

## Custom

You can also implement your custom serializer. To support both serialization and
Expand Down