Skip to content

Fix default parameters missing from RestResponse.Request.Parameters#2349

Open
alexeyzimarev wants to merge 5 commits intodevfrom
fix/default-params-in-response
Open

Fix default parameters missing from RestResponse.Request.Parameters#2349
alexeyzimarev wants to merge 5 commits intodevfrom
fix/default-params-in-response

Conversation

@alexeyzimarev
Copy link
Member

Summary

  • Fixes RestClient default headers missing from RestResponse.Request.Parameters #2282 — default parameters added via AddDefaultHeader() / AddDefaultParameter() were not visible in RestResponse.Request.Parameters
  • Merges default parameters into RestRequest.Parameters early in ExecuteRequestAsync, before request building, so they are accessible on the response
  • Removes the now-redundant separate default parameter merges in RequestContent, BuildUriExtensions, RequestHeaders, and OAuth1Authenticator

Test plan

  • New integration test Default_headers_should_appear_in_response_request_parameters verifies default headers appear in response.Request.Parameters
  • All 236 unit tests pass across net48/net8.0/net9.0/net10.0
  • All integration tests pass (except pre-existing NTLM test failure on net8.0+)
  • Verify request reuse scenario (executing the same RestRequest twice doesn't duplicate default params)

🤖 Generated with Claude Code

…2282)

Merge default parameters into RestRequest.Parameters early in
ExecuteRequestAsync so they are visible via response.Request.Parameters.
Remove the now-redundant separate merges in RequestContent,
BuildUriExtensions, RequestHeaders, and OAuth1Authenticator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qodo-free-for-open-source-projects
Copy link
Contributor

Review Summary by Qodo

Fix default parameters missing from RestResponse.Request.Parameters

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Merges default parameters into RestRequest early in ExecuteRequestAsync
• Makes default headers visible in RestResponse.Request.Parameters collection
• Removes redundant default parameter merges from multiple components
• Adds integration test verifying default headers appear in response
Diagram
flowchart LR
  A["ExecuteRequestAsync"] -->|"Merge default params"| B["RestRequest.Parameters"]
  B -->|"Now includes defaults"| C["RestResponse.Request.Parameters"]
  D["RequestContent"] -->|"Removed redundant merge"| E["Simplified"]
  F["BuildUriExtensions"] -->|"Removed redundant merge"| E
  G["RequestHeaders"] -->|"Removed redundant merge"| E
  H["OAuth1Authenticator"] -->|"Removed redundant merge"| E
Loading

Grey Divider

File Changes

1. src/RestSharp/RestClient.Async.cs 🐞 Bug fix +8/-1

Merge default parameters early in request execution

• Merges default parameters into request.Parameters early in ExecuteRequestAsync before request
 building
• Checks for duplicate parameters by name and type to avoid duplication on request reuse
• Removes redundant AddHeaders call for DefaultParameters in RequestHeaders initialization

src/RestSharp/RestClient.Async.cs


2. src/RestSharp/Request/RequestContent.cs 🐞 Bug fix +1/-1

Remove redundant default parameter merge

• Simplifies _parameters initialization to use only request.Parameters instead of union with
 DefaultParameters
• Removes redundant parameter merging since defaults are now merged earlier

src/RestSharp/Request/RequestContent.cs


3. src/RestSharp/BuildUriExtensions.cs 🐞 Bug fix +2/-5

Remove redundant default parameter handling

• Removes DefaultParameters from BuildUriWithoutQueryParameters method
• Removes DefaultParameters from GetRequestQuery method
• Simplifies parameter collection to use only request.Parameters

src/RestSharp/BuildUriExtensions.cs


View more (2)
4. src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs 🐞 Bug fix +0/-1

Remove redundant default parameter merge

• Removes redundant AddRange call for DefaultParameters in AddOAuthData method
• Keeps only request.Parameters since defaults are now pre-merged

src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs


5. test/RestSharp.Tests.Integrated/HttpHeadersTests.cs 🧪 Tests +19/-0

Add integration test for default headers visibility

• Adds new integration test Default_headers_should_appear_in_response_request_parameters
• Verifies that default headers added via AddDefaultHeader appear in response.Request.Parameters
• Tests both presence and correct value of default header in response

test/RestSharp.Tests.Integrated/HttpHeadersTests.cs


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Feb 25, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (1)

Grey Divider


Action required

1. Multi-value default headers lost📎 Requirement gap ✓ Correctness
Description
The new default-parameter merge deduplicates by Name+Type, which drops additional default
headers/parameters that intentionally share the same name (e.g., multiple header values). This can
cause missing default headers in RestResponse.Request.Parameters and potentially alters the
headers actually sent.
Code

src/RestSharp/RestClient.Async.cs[R105-109]

+        foreach (var defaultParam in DefaultParameters) {
+            if (!request.Parameters.Any(p => p.Name == defaultParam.Name && p.Type == defaultParam.Type)) {
+                request.Parameters.AddParameter(defaultParam);
+            }
+        }
Evidence
Compliance requires default headers added via AddDefaultHeader to appear in
RestResponse.Request.Parameters. The new merge logic skips adding any default parameter once one
with the same Name+Type exists, which prevents multiple default headers with the same name from
being represented (even though default headers are allowed to have multiple values).

Include RestClient default headers in RestResponse.Request.Parameters
src/RestSharp/RestClient.Async.cs[104-109]
src/RestSharp/Options/RestClientOptions.cs[218-222]
src/RestSharp/Parameters/DefaultParameters.cs[29-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The merge of `DefaultParameters` into `request.Parameters` currently deduplicates purely by `Name` + `Type`, which drops additional default headers/parameters that intentionally share a name (e.g., multiple `HttpHeader` values). This can prevent all configured default headers from appearing in `response.Request.Parameters` and can change what is actually sent.
## Issue Context
`RestClientOptions.AllowMultipleDefaultParametersWithSameName` explicitly does not apply to headers (multiple values are allowed), and `DefaultParameters.AddParameter` also allows duplicates for `HttpHeader` and certain multi-parameter types. The merge should respect those semantics.
## Fix Focus Areas
- src/RestSharp/RestClient.Async.cs[104-109]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. BuildUri skips defaults🐞 Bug ✓ Correctness
Description
BuildUriWithoutQueryParameters and GetRequestQuery no longer include client.DefaultParameters,
so client.BuildUri* omits default URL segments and query parameters unless the request was
previously executed/mutated. This is a behavioral regression for a public API that’s expected to
reflect defaults used “on every request.”
Code

src/RestSharp/BuildUriExtensions.cs[R53-58]

           var (uri, resource) = client.Options.BaseUrl.GetUrlSegmentParamsValues(
               request.Resource,
               client.Options.Encode,
-                request.Parameters,
-                client.DefaultParameters
+                request.Parameters
           );
           return uri.MergeBaseUrlAndResource(resource);
Evidence
BuildUriString delegates to BuildUriWithoutQueryParameters and GetRequestQuery, both of which
now only look at request.Parameters. Meanwhile, DefaultParameters are defined as client-level
defaults used on every request, and the URL-segment replacement helper supports multiple parameter
collections (which previously enabled passing both request and defaults).

src/RestSharp/BuildUriExtensions.cs[35-43]
src/RestSharp/BuildUriExtensions.cs[50-58]
src/RestSharp/BuildUriExtensions.cs[66-70]
src/RestSharp/IRestClient.cs[31-35]
src/RestSharp/Request/UriExtensions.cs[50-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`BuildUri*` no longer considers `client.DefaultParameters`, so URLs built via `client.BuildUri()` / `BuildUriString()` can omit default query params and URL segments.
### Issue Context
These methods are public API and are commonly used to inspect/preview the final request URI.
### Fix Focus Areas
- src/RestSharp/BuildUriExtensions.cs[50-58]
- src/RestSharp/BuildUriExtensions.cs[66-70]
- src/RestSharp/Request/UriExtensions.cs[50-66]
- src/RestSharp/IRestClient.cs[31-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. OAuth1 skips defaults🐞 Bug ✓ Correctness
Description
OAuth1 signing no longer includes client.DefaultParameters when OAuth1Authenticator.Authenticate
(or AddOAuthData) is called directly, producing incorrect signatures when defaults contain
query/post params. This is a realistic scenario since tests call Authenticate directly (outside
ExecuteRequestAsync).
Code

src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs[R258-262]

               ? x => BaseQuery(x) && x.Name != null && x.Name.StartsWith("oauth_")
               : (Func<Parameter, bool>)BaseQuery;

-        parameters.AddRange(client.DefaultParameters.Where(query).ToWebParameters());
       parameters.AddRange(request.Parameters.Where(query).ToWebParameters());
Evidence
AddOAuthData now only uses request.Parameters to build the OAuth signature parameter set. In the
normal RestClient.ExecuteRequestAsync path, defaults are merged before calling Authenticate, but
when Authenticate is invoked directly, that merge never runs; the test suite demonstrates direct
Authenticate usage, implying it’s supported/expected.

src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs[256-262]
src/RestSharp/RestClient.Async.cs[104-117]
test/RestSharp.Tests/Auth/OAuth1Tests.cs[43-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
OAuth1 signature generation no longer considers `client.DefaultParameters` when the authenticator is used directly, which can generate incorrect signatures for clients configured with default query/post parameters.
### Issue Context
The library test suite calls `authenticator.Authenticate(client, request)` directly, so direct usage is part of supported workflows.
### Fix Focus Areas
- src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs[256-262]
- src/RestSharp/RestClient.Async.cs[104-117]
- test/RestSharp.Tests/Auth/OAuth1Tests.cs[43-50]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Mutates request instance🐞 Bug ⛯ Reliability
Description
ExecuteRequestAsync permanently mutates the caller-provided RestRequest by adding default
parameters, which can make request reuse produce stale/incorrect defaults and can leak one client’s
defaults into subsequent executions. This also makes behavior depend on whether the request has been
executed before.
Code

src/RestSharp/RestClient.Async.cs[R104-109]

+        // Merge default parameters into the request so they are visible in response.Request.Parameters
+        foreach (var defaultParam in DefaultParameters) {
+            if (!request.Parameters.Any(p => p.Name == defaultParam.Name && p.Type == defaultParam.Type)) {
+                request.Parameters.AddParameter(defaultParam);
+            }
+        }
Evidence
The merge writes directly into the RestRequest.Parameters collection, and the response holds a
reference to the same RestRequest instance; therefore, the mutation persists beyond execution.
RestRequest.Parameters is a long-lived property on the request object, so subsequent uses of the
same request will include the previously merged defaults (and the merge’s de-dupe logic will prevent
updated defaults from being applied).

src/RestSharp/RestClient.Async.cs[104-109]
src/RestSharp/Request/RestRequest.cs[106-112]
src/RestSharp/Response/RestResponseBase.cs[29-33]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ExecuteRequestAsync` currently mutates the caller-provided `RestRequest` by injecting defaults, which can cause surprising/stale behavior when the same `RestRequest` instance is reused.
### Issue Context
`RestResponse` stores the same `RestRequest` instance reference, so any mutation during execution becomes observable and persistent.
### Fix Focus Areas
- src/RestSharp/RestClient.Async.cs[104-109]
- src/RestSharp/Request/RestRequest.cs[106-112]
- src/RestSharp/Response/RestResponseBase.cs[29-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 25, 2026

Deploying restsharp with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0015698
Status: ✅  Deploy successful!
Preview URL: https://1f7b314a.restsharp.pages.dev
Branch Preview URL: https://fix-default-params-in-respon.restsharp.pages.dev

View logs

@github-actions
Copy link

github-actions bot commented Feb 25, 2026

Test Results

   42 files     42 suites   18m 25s ⏱️
  501 tests   501 ✅ 0 💤 0 ❌
3 502 runs  3 502 ✅ 0 💤 0 ❌

Results for commit 2ebe93c.

♻️ This comment has been updated with latest results.

Comment on lines 53 to 58
var (uri, resource) = client.Options.BaseUrl.GetUrlSegmentParamsValues(
request.Resource,
client.Options.Encode,
request.Parameters,
client.DefaultParameters
request.Parameters
);
return uri.MergeBaseUrlAndResource(resource);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Builduri skips defaults 🐞 Bug ✓ Correctness

BuildUriWithoutQueryParameters and GetRequestQuery no longer include client.DefaultParameters,
so client.BuildUri* omits default URL segments and query parameters unless the request was
previously executed/mutated. This is a behavioral regression for a public API that’s expected to
reflect defaults used “on every request.”
Agent Prompt
### Issue description
`BuildUri*` no longer considers `client.DefaultParameters`, so URLs built via `client.BuildUri()` / `BuildUriString()` can omit default query params and URL segments.

### Issue Context
These methods are public API and are commonly used to inspect/preview the final request URI.

### Fix Focus Areas
- src/RestSharp/BuildUriExtensions.cs[50-58]
- src/RestSharp/BuildUriExtensions.cs[66-70]
- src/RestSharp/Request/UriExtensions.cs[50-66]
- src/RestSharp/IRestClient.cs[31-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@alexeyzimarev
Copy link
Member Author

Code review

Found 1 issue:

  1. Dedup logic silently drops multi-value default parameters. DefaultParameters explicitly allows multiple entries with the same name for HttpHeader, QueryString, and GetOrPost types (via AllowMultipleDefaultParametersWithSameName and MultiParameterTypes in DefaultParameters.cs). The dedup check !request.Parameters.Any(p => p.Name == defaultParam.Name && p.Type == defaultParam.Type) will add only the first matching default parameter; all subsequent defaults with the same name+type are blocked. For example, two default query params filter=active and filter=verified — only the first is kept. The previous code handled this correctly: RequestHeaders.AddHeaders used AddRange for all matching entries, and GetRequestQuery used SelectMany over both collections without dedup.

// Merge default parameters into the request so they are visible in response.Request.Parameters
foreach (var defaultParam in DefaultParameters) {
if (!request.Parameters.Any(p => p.Name == defaultParam.Name && p.Type == defaultParam.Type)) {
request.Parameters.AddParameter(defaultParam);
}
}

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Revert per-site merging to restore original design and fix three bugs:
1. Multi-value dedup — same-name defaults (AllowMultipleDefaultParametersWithSameName) were silently dropped
2. Public API breakage — BuildUriString/GetRequestQuery didn't include defaults when called outside ExecuteAsync
3. Request mutation — stale defaults persisted on reused requests when DefaultParameters changed

Add MergedParameters property on RestResponse to satisfy the original #2282 requirement
of making default parameters visible after execution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexeyzimarev
Copy link
Member Author

Updated: Fix default parameter merging bugs

This push fixes three bugs introduced by the original merge-into-request approach:

  1. Multi-value dedup — same-name defaults (AllowMultipleDefaultParametersWithSameName) were silently dropped
  2. Public API breakageBuildUriString/GetRequestQuery didn't include defaults when called outside ExecuteAsync
  3. Request mutation — stale defaults persisted on reused requests when DefaultParameters changed

Approach

Test results

  • ✅ All 236 unit tests pass
  • ✅ All integrated tests pass (except pre-existing NTLM env failures)
  • ✅ Three new regression tests + one MergedParameters test all pass

The addition of RestSharp.slnx alongside RestSharp.sln causes MSB1011
("more than one project or solution file") when running bare dotnet test.
Run each test project explicitly instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alexeyzimarev
Copy link
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

2282 - Partially compliant

Compliant requirements:

  • Add/adjust tests to cover the regression (default headers/parameters visibility on the response).

Non-compliant requirements:

  • Ensure default headers added via RestClient.AddDefaultHeader() are included in RestResponse.Request.Parameters.
  • Provide a way to inspect default parameters added via AddDefaultParameter() in the response request parameter collection as executed.

Requires further human verification:

  • Confirm whether the intended public contract is to mutate RestResponse.Request.Parameters itself (as requested by the ticket) or to introduce a new response property (MergedParameters) and update documentation accordingly.
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Behavior Mismatch

The fix populates response.MergedParameters but does not appear to update response.Request.Parameters. The ticket explicitly expects defaults to be present in RestResponse.Request.Parameters, so this may not satisfy the reported bug unless the rest of the codebase was also changed to make Request.Parameters include defaults at execution time.

public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationToken cancellationToken = default) {
    using var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);

    var response = internalResponse.Exception == null
        ? await RestResponse.FromHttpResponse(
                internalResponse.ResponseMessage!,
                request,
                Options,
                internalResponse.CookieContainer?.GetCookies(internalResponse.Url),
                cancellationToken
            )
            .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;
}
Duplication Risk

request.Parameters.Union(DefaultParameters) may yield duplicates or unexpected results depending on Parameter equality semantics (and ordering). If Union doesn't de-dup by name/type/value as expected, MergedParameters could contain repeated parameters, diverging from actual request behavior (especially for headers and for AllowMultipleDefaultParametersWithSameName scenarios).

public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationToken cancellationToken = default) {
    using var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);

    var response = internalResponse.Exception == null
        ? await RestResponse.FromHttpResponse(
                internalResponse.ResponseMessage!,
                request,
                Options,
                internalResponse.CookieContainer?.GetCookies(internalResponse.Url),
                cancellationToken
            )
            .ConfigureAwait(false)
        : GetErrorResponse(request, internalResponse.Exception, internalResponse.TimeoutToken);
    response.MergedParameters = new RequestParameters(request.Parameters.Union(DefaultParameters));
    await OnAfterRequest(response, cancellationToken).ConfigureAwait(false);
API Design

Adding MergedParameters as a nullable public property introduces a new API surface. Consider whether it should be non-null (initialized to an empty collection), whether it belongs on RestResponseBase vs a more specific type, and whether its mutability (set;) is desired or should be restricted to avoid consumers inadvertently altering response state.

/// <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; set; }
📄 References
  1. No matching references available

- Initialize to empty RequestParameters() so consumers never need null checks
- Restrict setter to internal to prevent external mutation of response state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RestClient default headers missing from RestResponse.Request.Parameters

1 participant