Skip to content

Commit 567c723

Browse files
Use CultureInfo.InvariantCulture for IFormattable types in AddParameter methods
Co-authored-by: alexeyzimarev <[email protected]>
1 parent 7f9d4c5 commit 567c723

File tree

7 files changed

+226
-9
lines changed

7 files changed

+226
-9
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) .NET Foundation and Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Globalization;
16+
17+
namespace RestSharp.Extensions;
18+
19+
static class ObjectExtensions {
20+
/// <summary>
21+
/// Converts a value to its string representation using invariant culture for IFormattable types.
22+
/// </summary>
23+
/// <typeparam name="T">The type of value to convert</typeparam>
24+
/// <param name="value">The value to convert</param>
25+
/// <returns>String representation using invariant culture, or null if value is null</returns>
26+
internal static string? ToStringInvariant<T>(this T value) => value switch {
27+
null => null,
28+
IFormattable f => f.ToString(null, CultureInfo.InvariantCulture),
29+
_ => value.ToString()
30+
};
31+
}

src/RestSharp/Parameters/ObjectParser.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
// limitations under the License.
1414
//
1515

16+
using System.Globalization;
1617
using System.Reflection;
18+
using RestSharp.Extensions;
1719

1820
namespace RestSharp;
1921

@@ -72,7 +74,7 @@ IEnumerable<ParsedParameter> GetArray(PropertyInfo propertyInfo, object? value)
7274
bool IsAllowedProperty(string propertyName)
7375
=> includedProperties.Length == 0 || includedProperties.Length > 0 && includedProperties.Contains(propertyName);
7476

75-
string? ParseValue(string? format, object? value) => format == null ? value?.ToString() : string.Format($"{{0:{format}}}", value);
77+
string? ParseValue(string? format, object? value) => format == null ? value.ToStringInvariant() : string.Format(CultureInfo.InvariantCulture, $"{{0:{format}}}", value);
7678
}
7779
}
7880

src/RestSharp/Parameters/Parameter.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System.Diagnostics;
16+
using RestSharp.Extensions;
1617

1718
namespace RestSharp;
1819

@@ -76,10 +77,10 @@ protected Parameter(string? name, object? value, ParameterType type, bool encode
7677
public static Parameter CreateParameter(string? name, object? value, ParameterType type, bool encode = true)
7778
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
7879
=> type switch {
79-
ParameterType.GetOrPost => new GetOrPostParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString(), encode),
80-
ParameterType.UrlSegment => new UrlSegmentParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString()!, encode),
81-
ParameterType.HttpHeader => new HeaderParameter(name!, value?.ToString()!),
82-
ParameterType.QueryString => new QueryParameter(Ensure.NotEmptyString(name, nameof(name)), value?.ToString(), encode),
80+
ParameterType.GetOrPost => new GetOrPostParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant(), encode),
81+
ParameterType.UrlSegment => new UrlSegmentParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant()!, encode),
82+
ParameterType.HttpHeader => new HeaderParameter(name!, value.ToStringInvariant()!),
83+
ParameterType.QueryString => new QueryParameter(Ensure.NotEmptyString(name, nameof(name)), value.ToStringInvariant(), encode),
8384
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
8485
};
8586

src/RestSharp/Request/RestRequestExtensions.Query.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using RestSharp.Extensions;
16+
1517
namespace RestSharp;
1618

1719
public static partial class RestRequestExtensions {
@@ -37,6 +39,6 @@ public RestRequest AddQueryParameter(string name, string? value, bool encode = t
3739
/// <param name="encode">Encode the value or not, default true</param>
3840
/// <returns></returns>
3941
public RestRequest AddQueryParameter<T>(string name, T value, bool encode = true) where T : struct
40-
=> request.AddQueryParameter(name, value.ToString(), encode);
42+
=> request.AddQueryParameter(name, value.ToStringInvariant(), encode);
4143
}
4244
}

src/RestSharp/Request/RestRequestExtensions.Url.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using RestSharp.Extensions;
16+
1517
namespace RestSharp;
1618

1719
public static partial class RestRequestExtensions {
@@ -37,6 +39,6 @@ public RestRequest AddUrlSegment(string name, string? value, bool encode = true)
3739
/// <param name="encode">Encode the value or not, default true</param>
3840
/// <returns></returns>
3941
public RestRequest AddUrlSegment<T>(string name, T value, bool encode = true) where T : struct
40-
=> request.AddUrlSegment(name, value.ToString(), encode);
42+
=> request.AddUrlSegment(name, value.ToStringInvariant(), encode);
4143
}
4244
}

src/RestSharp/Request/RestRequestExtensions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using RestSharp.Extensions;
16+
1517
namespace RestSharp;
1618

1719
[PublicAPI]
@@ -52,7 +54,7 @@ public RestRequest AddParameter(string? name, object value, ParameterType type,
5254
/// <param name="encode">Encode the value or not, default true</param>
5355
/// <returns>This request</returns>
5456
public RestRequest AddParameter<T>(string name, T value, bool encode = true) where T : struct
55-
=> request.AddParameter(name, value.ToString(), encode);
57+
=> request.AddParameter(name, value.ToStringInvariant(), encode);
5658

5759
/// <summary>
5860
/// Adds or updates a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
@@ -72,7 +74,7 @@ public RestRequest AddOrUpdateParameter(string name, string? value, bool encode
7274
/// <param name="encode">Encode the value or not, default true</param>
7375
/// <returns>This request</returns>
7476
public RestRequest AddOrUpdateParameter<T>(string name, T value, bool encode = true) where T : struct
75-
=> request.AddOrUpdateParameter(name, value.ToString(), encode);
77+
=> request.AddOrUpdateParameter(name, value.ToStringInvariant(), encode);
7678

7779
RestRequest AddParameters(IEnumerable<Parameter> parameters) {
7880
request.Parameters.AddParameters(parameters);
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using System.Globalization;
2+
3+
namespace RestSharp.Tests.Parameters;
4+
5+
public class InvariantCultureParameterTests {
6+
[Fact]
7+
public void AddParameter_Double_UsesInvariantCulture() {
8+
// Save original culture
9+
var originalCulture = Thread.CurrentThread.CurrentCulture;
10+
try {
11+
// Set a culture that uses comma as decimal separator
12+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
13+
14+
var request = new RestRequest();
15+
request.AddParameter("value", 1.234);
16+
17+
var parameter = request.Parameters.First();
18+
parameter.Value.Should().Be("1.234");
19+
}
20+
finally {
21+
Thread.CurrentThread.CurrentCulture = originalCulture;
22+
}
23+
}
24+
25+
[Fact]
26+
public void AddOrUpdateParameter_Double_UsesInvariantCulture() {
27+
var originalCulture = Thread.CurrentThread.CurrentCulture;
28+
try {
29+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
30+
31+
var request = new RestRequest();
32+
request.AddOrUpdateParameter("value", 1.234);
33+
34+
var parameter = request.Parameters.First();
35+
parameter.Value.Should().Be("1.234");
36+
}
37+
finally {
38+
Thread.CurrentThread.CurrentCulture = originalCulture;
39+
}
40+
}
41+
42+
[Fact]
43+
public void AddQueryParameter_Double_UsesInvariantCulture() {
44+
var originalCulture = Thread.CurrentThread.CurrentCulture;
45+
try {
46+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
47+
48+
var request = new RestRequest();
49+
request.AddQueryParameter("value", 1.234);
50+
51+
var parameter = request.Parameters.First();
52+
parameter.Value.Should().Be("1.234");
53+
}
54+
finally {
55+
Thread.CurrentThread.CurrentCulture = originalCulture;
56+
}
57+
}
58+
59+
[Fact]
60+
public void AddUrlSegment_Double_UsesInvariantCulture() {
61+
var originalCulture = Thread.CurrentThread.CurrentCulture;
62+
try {
63+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
64+
65+
var request = new RestRequest("{value}");
66+
request.AddUrlSegment("value", 1.234);
67+
68+
var parameter = request.Parameters.First();
69+
parameter.Value.Should().Be("1.234");
70+
}
71+
finally {
72+
Thread.CurrentThread.CurrentCulture = originalCulture;
73+
}
74+
}
75+
76+
[Fact]
77+
public void CreateParameter_Double_UsesInvariantCulture() {
78+
var originalCulture = Thread.CurrentThread.CurrentCulture;
79+
try {
80+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
81+
82+
var parameter = Parameter.CreateParameter("value", 1.234, ParameterType.GetOrPost);
83+
84+
parameter.Value.Should().Be("1.234");
85+
}
86+
finally {
87+
Thread.CurrentThread.CurrentCulture = originalCulture;
88+
}
89+
}
90+
91+
[Fact]
92+
public void AddParameter_Decimal_UsesInvariantCulture() {
93+
var originalCulture = Thread.CurrentThread.CurrentCulture;
94+
try {
95+
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
96+
97+
var request = new RestRequest();
98+
request.AddParameter("value", 123.456m);
99+
100+
var parameter = request.Parameters.First();
101+
parameter.Value.Should().Be("123.456");
102+
}
103+
finally {
104+
Thread.CurrentThread.CurrentCulture = originalCulture;
105+
}
106+
}
107+
108+
[Fact]
109+
public void AddParameter_Float_UsesInvariantCulture() {
110+
var originalCulture = Thread.CurrentThread.CurrentCulture;
111+
try {
112+
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
113+
114+
var request = new RestRequest();
115+
request.AddParameter("value", 2.5f);
116+
117+
var parameter = request.Parameters.First();
118+
parameter.Value.Should().Be("2.5");
119+
}
120+
finally {
121+
Thread.CurrentThread.CurrentCulture = originalCulture;
122+
}
123+
}
124+
125+
[Fact]
126+
public void AddParameter_DateTime_UsesInvariantCulture() {
127+
var originalCulture = Thread.CurrentThread.CurrentCulture;
128+
try {
129+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
130+
131+
var dateTime = new DateTime(2024, 12, 25, 10, 30, 0, DateTimeKind.Unspecified);
132+
var request = new RestRequest();
133+
request.AddParameter("date", dateTime);
134+
135+
var parameter = request.Parameters.First();
136+
// DateTime.ToString with InvariantCulture uses MM/dd/yyyy format
137+
parameter.Value.Should().Be("12/25/2024 10:30:00");
138+
}
139+
finally {
140+
Thread.CurrentThread.CurrentCulture = originalCulture;
141+
}
142+
}
143+
144+
[Fact]
145+
public void AddParameter_Integer_WorksWithAnyCulture() {
146+
var originalCulture = Thread.CurrentThread.CurrentCulture;
147+
try {
148+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
149+
150+
var request = new RestRequest();
151+
request.AddParameter("value", 12345);
152+
153+
var parameter = request.Parameters.First();
154+
parameter.Value.Should().Be("12345");
155+
}
156+
finally {
157+
Thread.CurrentThread.CurrentCulture = originalCulture;
158+
}
159+
}
160+
161+
[Fact]
162+
public void AddObject_WithDoubleProperty_UsesInvariantCulture() {
163+
var originalCulture = Thread.CurrentThread.CurrentCulture;
164+
try {
165+
Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
166+
167+
var request = new RestRequest();
168+
request.AddObject(new { Amount = 1.234 });
169+
170+
var parameter = request.Parameters.First();
171+
parameter.Value.Should().Be("1.234");
172+
}
173+
finally {
174+
Thread.CurrentThread.CurrentCulture = originalCulture;
175+
}
176+
}
177+
}

0 commit comments

Comments
 (0)