Skip to content

Conversation

@martincostello
Copy link
Member

@martincostello martincostello commented Jun 4, 2025

Use culture-invariant TextWriter implementation for OpenAPI.

Port of #62193 for .NET 9.

Description

To avoid any accidental changes to SignalR's use of Utf8BufferTextWriter I've removed the use of the writer pooling for OpenAPI and instead a new instance is created every time.

Fixes #60628

Customer Impact

Developers run into this issue when hosting under non-en cultures, but no workarounds are available.

Regression?

  • Yes
  • No (Bug has existed throughout 9.0 previews; not a regression from an earlier serviced release.)

Risk

  • High
  • Medium
  • Low

Change is isolated to the OpenAPI serialization path and uses an existing overload; no impact on runtime behavior or other subsystems.

Verification

  • Manual
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

Ensure OpenAPI documents are written to a culture-invariant `TextWriter` implementation.

Resolves dotnet#60628.
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jun 4, 2025
@dotnet-policy-service dotnet-policy-service bot added this to the 9.0.x milestone Jun 4, 2025
@martincostello martincostello added feature-openapi area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc labels Jun 4, 2025
@martincostello martincostello deleted the gh-60628-9 branch July 17, 2025 10:49
@dotnet-policy-service dotnet-policy-service bot removed this from the 9.0.x milestone Jul 17, 2025
@martincostello martincostello restored the gh-60628-9 branch August 1, 2025 09:11
@martincostello martincostello reopened this Aug 1, 2025
@captainsafia
Copy link
Member

@martincostello I updated the issue with the servicing template in case you'd like to review it and mark the PR as ready for review.

@martincostello martincostello marked this pull request as ready for review August 6, 2025 18:11
Copilot AI review requested due to automatic review settings August 6, 2025 18:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR addresses a culture-invariant formatting issue in OpenAPI document generation. When hosting applications under non-English cultures (like fr-FR), the OpenAPI serialization was producing incorrect JSON with locale-specific number formatting (e.g., using commas instead of dots for decimals).

  • Introduces a new constructor for Utf8BufferTextWriter that accepts an IFormatProvider
  • Updates the OpenAPI endpoint handler to use CultureInfo.InvariantCulture for serialization
  • Refactors test approach from direct document serialization to HTTP endpoint testing

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Utf8BufferTextWriter.cs Adds constructor overload accepting IFormatProvider for culture-invariant formatting
OpenApiEndpointRouteBuilderExtensions.cs Updates OpenAPI serialization to use invariant culture and creates new writer instance instead of pooled one
OpenApiDocumentIntegrationTests.cs Simplifies tests to use HTTP client instead of direct document service
Program.cs Adds middleware to set French culture for testing culture-invariant behavior
TestController.cs Adds test endpoint with float property to verify decimal formatting
Multiple snapshot files Updates test snapshots with new server URL and endpoint entries

@martincostello
Copy link
Member Author

@captainsafia Thanks, done. I wanted to make sure you were happy with the changes/approach first before trying to guide it through shiproom.

@captainsafia captainsafia added the Servicing-consider Shiproom approval is required for the issue label Aug 6, 2025
Copy link
Member

@captainsafia captainsafia left a comment

Choose a reason for hiding this comment

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

LGTM!

servers change to snapshots is expected since we move from resolving the document via the service to via the endpoint.

New test scenario matches what we have in main.

@leecow leecow added Servicing-approved Shiproom has approved the issue and removed Servicing-consider Shiproom approval is required for the issue labels Aug 12, 2025
@leecow leecow added this to the 9.0.10 milestone Aug 12, 2025
@danmoseley
Copy link
Member

@captainsafia tactics approved but asked the usual question about whether we scanned for variants (other instances)?

@wtgodbe
Copy link
Member

wtgodbe commented Aug 13, 2025

Holding this for October at this point

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Aug 20, 2025
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Sep 3, 2025
@wtgodbe
Copy link
Member

wtgodbe commented Sep 3, 2025

/azp run

This was referenced Nov 17, 2025
github-actions bot pushed a commit to saan800/github that referenced this pull request Nov 17, 2025
Updated
[Microsoft.AspNetCore.Mvc.Testing](https://github.com/dotnet/aspnetcore)
from 9.0.9 to 9.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Mvc.Testing's
releases](https://github.com/dotnet/aspnetcore/releases)._

## 9.0.11

[Release](https://github.com/dotnet/core/releases/tag/v9.0.11)

## What's Changed
* Update branding to 9.0.11 by @​vseanreesermsft in
dotnet/aspnetcore#63950
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63677
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63678
* [release/9.0] (deps): Bump src/submodules/googletest from `eb2d85e` to
`9706f75` by @​dependabot[bot] in
dotnet/aspnetcore#63894
* [release/9.0] Fixed devtools url used for debug with chrome and edge
by @​github-actions[bot] in
dotnet/aspnetcore#61948
* [release/9.0] (http2): Lower WINDOWS_UPDATE received on (half)closed
stream to stream abortion by @​DeagleGross in
dotnet/aspnetcore#63934
* [release/9.0] Re-quarantine
ServerRoutingTest.NavigationLock_OverlappingNavigationsCancelExistingNavigations_HistoryNavigation
by @​github-actions[bot] in
dotnet/aspnetcore#63956
* [release/9.0] Fix nginx install on mac, linux by @​wtgodbe in
dotnet/aspnetcore#63966
* [Hot Reload] Do not attempt to apply empty deltas. by @​tmat in
dotnet/aspnetcore#63979
* Merging internal commits for release/9.0 by @​vseanreesermsft in
dotnet/aspnetcore#64036
* Revert log level severity for unknown proxy in
ForwardedHeadersMiddleware by @​BrennanConroy in
dotnet/aspnetcore#64091
* Set timeoutInMinutes to 0 for Windows build job by @​vseanreesermsft
in dotnet/aspnetcore#64126


**Full Changelog**:
dotnet/aspnetcore@v9.0.10...v9.0.11

## 9.0.10

[Release](https://github.com/dotnet/core/releases/tag/v9.0.10)

## What's Changed
* Update branding to 9.0.10 by @​vseanreesermsft in
dotnet/aspnetcore#63510
* [9.0] Make duplicate deb/rpm packages so we can sign them with the new
PMC key by @​jkoritzinsky in
dotnet/aspnetcore#63249
* [release/9.0] Extend Unofficial 1ES template in IdentityModel nightly
tests job by @​github-actions[bot] in
dotnet/aspnetcore#63465
* [release/9.0] (deps): Bump src/submodules/googletest from `373af2e` to
`eb2d85e` by @​dependabot[bot] in
dotnet/aspnetcore#63501
* [release/9.0] Quarantine ResponseBody_WriteContentLength_PassedThrough
by @​wtgodbe in dotnet/aspnetcore#63533
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63304
* [release/9.0] [OpenAPI] Use invariant culture for TextWriter by
@​martincostello in dotnet/aspnetcore#62239
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63303
* Unquarantine `RadioButtonGetsResetAfterSubmittingEnhancedForm` by
@​ilonatommy in dotnet/aspnetcore#63556
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63577
* Merging internal commits for release/9.0 by @​vseanreesermsft in
dotnet/aspnetcore#63604
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63648
* backport(9.0): Fix runtime architecture detection logic in ANCM. by
@​DeagleGross in dotnet/aspnetcore#63707


**Full Changelog**:
dotnet/aspnetcore@v9.0.9...v9.0.10

Commits viewable in [compare
view](dotnet/aspnetcore@v9.0.9...v9.0.11).
</details>

Updated
[Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/aspnetcore)
from 9.0.9 to 9.0.11.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.OpenApi's
releases](https://github.com/dotnet/aspnetcore/releases)._

## 9.0.11

[Release](https://github.com/dotnet/core/releases/tag/v9.0.11)

## What's Changed
* Update branding to 9.0.11 by @​vseanreesermsft in
dotnet/aspnetcore#63950
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63677
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63678
* [release/9.0] (deps): Bump src/submodules/googletest from `eb2d85e` to
`9706f75` by @​dependabot[bot] in
dotnet/aspnetcore#63894
* [release/9.0] Fixed devtools url used for debug with chrome and edge
by @​github-actions[bot] in
dotnet/aspnetcore#61948
* [release/9.0] (http2): Lower WINDOWS_UPDATE received on (half)closed
stream to stream abortion by @​DeagleGross in
dotnet/aspnetcore#63934
* [release/9.0] Re-quarantine
ServerRoutingTest.NavigationLock_OverlappingNavigationsCancelExistingNavigations_HistoryNavigation
by @​github-actions[bot] in
dotnet/aspnetcore#63956
* [release/9.0] Fix nginx install on mac, linux by @​wtgodbe in
dotnet/aspnetcore#63966
* [Hot Reload] Do not attempt to apply empty deltas. by @​tmat in
dotnet/aspnetcore#63979
* Merging internal commits for release/9.0 by @​vseanreesermsft in
dotnet/aspnetcore#64036
* Revert log level severity for unknown proxy in
ForwardedHeadersMiddleware by @​BrennanConroy in
dotnet/aspnetcore#64091
* Set timeoutInMinutes to 0 for Windows build job by @​vseanreesermsft
in dotnet/aspnetcore#64126


**Full Changelog**:
dotnet/aspnetcore@v9.0.10...v9.0.11

## 9.0.10

[Release](https://github.com/dotnet/core/releases/tag/v9.0.10)

## What's Changed
* Update branding to 9.0.10 by @​vseanreesermsft in
dotnet/aspnetcore#63510
* [9.0] Make duplicate deb/rpm packages so we can sign them with the new
PMC key by @​jkoritzinsky in
dotnet/aspnetcore#63249
* [release/9.0] Extend Unofficial 1ES template in IdentityModel nightly
tests job by @​github-actions[bot] in
dotnet/aspnetcore#63465
* [release/9.0] (deps): Bump src/submodules/googletest from `373af2e` to
`eb2d85e` by @​dependabot[bot] in
dotnet/aspnetcore#63501
* [release/9.0] Quarantine ResponseBody_WriteContentLength_PassedThrough
by @​wtgodbe in dotnet/aspnetcore#63533
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63304
* [release/9.0] [OpenAPI] Use invariant culture for TextWriter by
@​martincostello in dotnet/aspnetcore#62239
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63303
* Unquarantine `RadioButtonGetsResetAfterSubmittingEnhancedForm` by
@​ilonatommy in dotnet/aspnetcore#63556
* [release/9.0] Update dependencies from dotnet/extensions by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63577
* Merging internal commits for release/9.0 by @​vseanreesermsft in
dotnet/aspnetcore#63604
* [release/9.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/aspnetcore#63648
* backport(9.0): Fix runtime architecture detection logic in ANCM. by
@​DeagleGross in dotnet/aspnetcore#63707


**Full Changelog**:
dotnet/aspnetcore@v9.0.9...v9.0.10

Commits viewable in [compare
view](dotnet/aspnetcore@v9.0.9...v9.0.11).
</details>

Updated [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest)
from 18.0.0 to 18.0.1.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.NET.Test.Sdk's
releases](https://github.com/microsoft/vstest/releases)._

## 18.0.1

## What's Changed

Fixing an issue with loading covrun64.dll on systems that have .NET 10
SDK installed:
https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/code-coverage-dynamic-native-instrumentation

* Disable DynamicNative instrumentation by default by @​nohwnd in
microsoft/vstest#15298
* Update MicrosoftInternalCodeCoveragePackageVersion to 18.0.6 by
@​nohwnd in microsoft/vstest#15312

### Internal changes

* Update VersionPrefix to 18.0.1 by @​nohwnd in
microsoft/vstest#15301
* Update build tools to 17.8.43 by @​nohwnd in
microsoft/vstest#15305



**Full Changelog**:
microsoft/vstest@v18.0.0...v18.0.1

Commits viewable in [compare
view](microsoft/vstest@v18.0.0...v18.0.1).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc community-contribution Indicates that the PR has been added by a community member feature-openapi Servicing-approved Shiproom has approved the issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants