diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 92807d7e3..12eb9b98d 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -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
@@ -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
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 000000000..615e58179
--- /dev/null
+++ b/CLAUDE.md
@@ -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
\ No newline at end of file
diff --git a/RestSharp.slnx b/RestSharp.slnx
new file mode 100644
index 000000000..d06f32574
--- /dev/null
+++ b/RestSharp.slnx
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/docs/usage/response.md b/docs/docs/usage/response.md
index dbaf302b4..43745ec39 100644
--- a/docs/docs/usage/response.md
+++ b/docs/docs/usage/response.md
@@ -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` has one additional property:
diff --git a/src/RestSharp/Response/RestResponseBase.cs b/src/RestSharp/Response/RestResponseBase.cs
index 383895fb4..b54ed4e17 100644
--- a/src/RestSharp/Response/RestResponseBase.cs
+++ b/src/RestSharp/Response/RestResponseBase.cs
@@ -132,6 +132,12 @@ protected RestResponseBase(RestRequest request) {
///
public Version? Version { get; set; }
+ ///
+ /// 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.
+ ///
+ public ParametersCollection MergedParameters { get; internal set; } = new RequestParameters();
+
///
/// Root element of the serialized response content, only works if deserializer supports it
///
diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs
index df4558795..a6142d07a 100644
--- a/src/RestSharp/RestClient.Async.cs
+++ b/src/RestSharp/RestClient.Async.cs
@@ -36,6 +36,7 @@ public async Task 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;
diff --git a/test/RestSharp.Tests.Integrated/DefaultParameterTests.cs b/test/RestSharp.Tests.Integrated/DefaultParameterTests.cs
index c2038f6a3..da058b238 100644
--- a/test/RestSharp.Tests.Integrated/DefaultParameterTests.cs
+++ b/test/RestSharp.Tests.Integrated/DefaultParameterTests.cs
@@ -4,7 +4,8 @@
namespace RestSharp.Tests.Integrated;
public sealed class DefaultParameterTests(WireMockTestServer server) : IClassFixture {
- 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() {
@@ -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");
+ }
}
\ No newline at end of file
diff --git a/test/RestSharp.Tests.Integrated/HttpHeadersTests.cs b/test/RestSharp.Tests.Integrated/HttpHeadersTests.cs
index 559c6435a..2a0a60084 100644
--- a/test/RestSharp.Tests.Integrated/HttpHeadersTests.cs
+++ b/test/RestSharp.Tests.Integrated/HttpHeadersTests.cs
@@ -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 response, Header header) {
var h = FindHeader(response, header.Name);
h.Should().NotBeNull();