Skip to content
Merged
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
16 changes: 14 additions & 2 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ jobs:
9.0.x
10.0.x
- name: Run tests
run: dotnet test -c Debug -f ${{ matrix.dotnet }}
run: |
dotnet test test/RestSharp.Tests -c Debug -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Integrated -c Debug -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Json -c Debug -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Xml -c Debug -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Csv -c Debug -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.DependencyInjection -c Debug -f ${{ matrix.dotnet }}
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v6
Expand Down Expand Up @@ -65,7 +71,13 @@ jobs:
9.0.x
10.0.x
- name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
run: |
dotnet test test/RestSharp.Tests -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Integrated -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Json -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Xml -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.Serializers.Csv -f ${{ matrix.dotnet }}
dotnet test test/RestSharp.Tests.DependencyInjection -f ${{ matrix.dotnet }}
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v6
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## MCP Servers Available
- mem0: Use this AI memory for storing and retrieving long-term context as well as short-term context
65 changes: 65 additions & 0 deletions RestSharp.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<Solution>
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="Debug.Appveyor" />
<BuildType Name="Release" />
<Platform Name="Any CPU" />
<Platform Name="ARM" />
<Platform Name="Mixed Platforms" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/Perf/">
<Project Path="benchmarks/RestSharp.Benchmarks/RestSharp.Benchmarks.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
</Folder>
<Folder Name="/Serializers/">
<Project Path="src/RestSharp.Serializers.CsvHelper/RestSharp.Serializers.CsvHelper.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="src/RestSharp.Serializers.NewtonsoftJson/RestSharp.Serializers.NewtonsoftJson.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="src/RestSharp.Serializers.Xml/RestSharp.Serializers.Xml.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
</Folder>
<Folder Name="/SourceGen/">
<Project Path="gen/SourceGenerator/SourceGenerator.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
</Folder>
<Folder Name="/Tests/">
<Project Path="test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.DependencyInjection/RestSharp.Tests.DependencyInjection.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.Integrated/RestSharp.Tests.Integrated.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.Serializers.Csv/RestSharp.Tests.Serializers.Csv.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.Serializers.Json/RestSharp.Tests.Serializers.Json.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.Serializers.Xml/RestSharp.Tests.Serializers.Xml.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests.Shared/RestSharp.Tests.Shared.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="test/RestSharp.Tests/RestSharp.Tests.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
</Folder>
<Project Path="src/RestSharp.Extensions.DependencyInjection/RestSharp.Extensions.DependencyInjection.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
<Project Path="src/RestSharp/RestSharp.csproj">
<BuildType Solution="Debug.Appveyor|*" Project="Debug" />
</Project>
</Solution>
13 changes: 13 additions & 0 deletions docs/docs/usage/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ Response object contains the following properties:
| `ErrorException` | `Exception?` | Exception thrown when executing the request, if any. |
| `Version` | `Version?` | HTTP protocol version of the request. |
| `RootElement` | `string?` | Root element of the serialized response content, only works if deserializer supports it. |
| `MergedParameters` | `ParametersCollection` | Combined view of request parameters and client default parameters at execution time. |

### Merged parameters

The `MergedParameters` property provides a combined view of the request's own parameters and the client's [default parameters](request.md#request-headers) as they were at execution time. This is useful for logging or debugging the full set of parameters that were applied to a request, since `Request.Parameters` only contains the parameters added directly to the request.

```csharp
var response = await client.ExecuteAsync(request);

foreach (var param in response.MergedParameters) {
Console.WriteLine($"{param.Name} = {param.Value} ({param.Type})");
}
```

In addition, `RestResponse<T>` has one additional property:

Expand Down
6 changes: 6 additions & 0 deletions src/RestSharp/Response/RestResponseBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ protected RestResponseBase(RestRequest request) {
/// </summary>
public Version? Version { get; set; }

/// <summary>
/// Combined view of request parameters and client default parameters as they were at execution time.
/// Use this to inspect the full set of parameters that were applied to the request.
/// </summary>
public ParametersCollection MergedParameters { get; internal set; } = new RequestParameters();

/// <summary>
/// Root element of the serialized response content, only works if deserializer supports it
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/RestSharp/RestClient.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
)
.ConfigureAwait(false)
: GetErrorResponse(request, internalResponse.Exception, internalResponse.TimeoutToken);
response.MergedParameters = new RequestParameters(request.Parameters.Union(DefaultParameters));
await OnAfterRequest(response, cancellationToken).ConfigureAwait(false);

return Options.ThrowOnAnyError ? response.ThrowIfError() : response;
Expand Down
73 changes: 72 additions & 1 deletion test/RestSharp.Tests.Integrated/DefaultParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
namespace RestSharp.Tests.Integrated;

public sealed class DefaultParameterTests(WireMockTestServer server) : IClassFixture<WireMockTestServer> {
readonly RequestBodyCapturer _capturer = server.ConfigureBodyCapturer(Method.Get, false);
readonly RequestBodyCapturer _capturer = server.ConfigureBodyCapturer(Method.Get, false);
readonly RequestBodyCapturer _capturerOnPath = server.ConfigureBodyCapturer(Method.Get);

[Fact]
public async Task Should_add_default_and_request_query_get_parameters() {
Expand Down Expand Up @@ -48,4 +49,74 @@ public async Task Should_not_encode_pipe_character_when_encode_is_false() {
var query = _capturer.RawUrl.Split('?')[1];
query.Should().Contain("ids=in:001|116");
}

[Fact]
public async Task Should_include_multiple_default_query_params_with_same_name() {
using var client = new RestClient(
new RestClientOptions(server.Url!) { AllowMultipleDefaultParametersWithSameName = true }
);
client.AddDefaultParameter("filter", "active", ParameterType.QueryString);
client.AddDefaultParameter("filter", "verified", ParameterType.QueryString);

var request = new RestRequest("capture");
await client.GetAsync(request);

var query = _capturerOnPath.Url!.Query;
query.Should().Contain("filter=active");
query.Should().Contain("filter=verified");
}

[Fact]
public async Task Should_include_default_query_params_in_BuildUriString_without_executing() {
using var client = new RestClient(server.Url!);
client.AddDefaultParameter("foo", "bar", ParameterType.QueryString);

var request = new RestRequest("resource");
var uri = client.BuildUriString(request);

uri.Should().Contain("foo=bar");
}

[Fact]
public async Task Should_not_permanently_mutate_request_parameters_after_execute() {
using var client = new RestClient(server.Url!);
client.AddDefaultParameter("default_key", "default_val", ParameterType.QueryString);

var request = new RestRequest("capture");
var paramsBefore = request.Parameters.Count;

await client.GetAsync(request);

// Request parameters should not have been mutated by the execution.
request.Parameters.Count.Should().Be(paramsBefore);

// Now replace the default parameter with a different value.
client.DefaultParameters.ReplaceParameter(new QueryParameter("default_key", "updated_val"));

await client.GetAsync(request);

// The second execution should use the updated default value, not the stale one.
var query = _capturerOnPath.Url!.Query;
query.Should().Contain("default_key=updated_val");
query.Should().NotContain("default_key=default_val");
}

[Fact]
public async Task Should_include_default_params_in_merged_parameters_on_response() {
using var client = new RestClient(server.Url!);
client.AddDefaultParameter("default_key", "default_val", ParameterType.QueryString);

var request = new RestRequest("capture").AddQueryParameter("req_key", "req_val");
var response = await client.ExecuteAsync(request);

var defaultParam = response.MergedParameters
.FirstOrDefault(p => p.Name == "default_key" && p.Type == ParameterType.QueryString);
defaultParam.Should().NotBeNull();
defaultParam!.Value.Should().Be("default_val");

var requestParam = response.MergedParameters
.FirstOrDefault(p => p.Name == "req_key" && p.Type == ParameterType.QueryString);
requestParam.Should().NotBeNull();
requestParam!.Value.Should().Be("req_val");
}
}
19 changes: 19 additions & 0 deletions test/RestSharp.Tests.Integrated/HttpHeadersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ public async Task Should_sent_custom_UserAgent() {
response.GetHeaderValue("Server").Should().Be("Kestrel");
}

[Fact]
public async Task Default_headers_should_appear_in_response_merged_parameters() {
const string headerName = "X-Custom-Default";
const string headerValue = "DefaultValue123";

_client.AddDefaultHeader(headerName, headerValue);

var request = new RestRequest("/headers");
var response = await _client.ExecuteAsync(request);

response.StatusCode.Should().Be(HttpStatusCode.OK);

var param = response.MergedParameters
.FirstOrDefault(p => p.Name == headerName && p.Type == ParameterType.HttpHeader);

param.Should().NotBeNull();
param!.Value.Should().Be(headerValue);
}

static void CheckHeader(RestResponse<TestServerResponse[]> response, Header header) {
var h = FindHeader(response, header.Name);
h.Should().NotBeNull();
Expand Down
Loading