Skip to content

Commit 0ef673e

Browse files
committed
Proposal: Add a TreatAsString field to QueryAttribute
The proposal arises from the need to pass custom types or structs which might represent types with rules and conversions. For example: ```csharp record readonly struct ZipCode(ushort ZipCode) { public override string ToString() => ZipCode.ToString(); } ``` To solve the problem the proposal seeks to add a field to the QueryAttribute called `TreatAsString`. The new field is used when the query string is created and simply calls the `ToString()` function on a type. There is a workaround that is not documented. The current workaround is to implement `IFormattable` on the type, which feels redundant if `ToString()` is already implemented on a custom type.
1 parent 860a332 commit 0ef673e

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

Refit.Tests/RequestBuilder.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,7 @@ Task QueryWithOptionalParameters(
20442044
int id,
20452045
[Query] string text = null,
20462046
[Query] int? optionalId = null,
2047+
[Query(TreatAsString = true)] Foo foo = null,
20472048
[Query(CollectionFormat = CollectionFormat.Multi)] string[] filters = null
20482049
);
20492050

@@ -3541,12 +3542,12 @@ string expectedQuery
35413542
}
35423543

35433544
[Theory]
3544-
[InlineData("/api/123?text=title&optionalId=999&filters=A&filters=B")]
3545+
[InlineData("/api/123?text=title&optionalId=999&foo=foo&filters=A&filters=B")]
35453546
public void TestNullableQueryStringParams(string expectedQuery)
35463547
{
35473548
var fixture = new RequestBuilderImplementation<IDummyHttpApi>();
35483549
var factory = fixture.BuildRequestFactoryForMethod("QueryWithOptionalParameters");
3549-
var output = factory(new object[] { 123, "title", 999, new string[] { "A", "B" } });
3550+
var output = factory(new object[] { 123, "title", 999, new Foo(), new string[] { "A", "B" } });
35503551

35513552
var uri = new Uri(new Uri("http://api"), output.RequestUri);
35523553
Assert.Equal(expectedQuery, uri.PathAndQuery);
@@ -3558,7 +3559,7 @@ public void TestNullableQueryStringParamsWithANull(string expectedQuery)
35583559
{
35593560
var fixture = new RequestBuilderImplementation<IDummyHttpApi>();
35603561
var factory = fixture.BuildRequestFactoryForMethod("QueryWithOptionalParameters");
3561-
var output = factory(new object[] { 123, "title", null, new string[] { "A", "B" } });
3562+
var output = factory(new object[] { 123, "title", null, null, new string[] { "A", "B" } });
35623563

35633564
var uri = new Uri(new Uri("http://api"), output.RequestUri);
35643565
Assert.Equal(expectedQuery, uri.PathAndQuery);
@@ -3944,6 +3945,14 @@ public void ComplexQueryObjectWithDictionaryAndCustomFormatterProducesCorrectQue
39443945
}
39453946
}
39463947

3948+
public record Foo
3949+
{
3950+
public override string ToString()
3951+
{
3952+
return "foo";
3953+
}
3954+
}
3955+
39473956
static class RequestBuilderTestExtensions
39483957
{
39493958
public static Func<object[], HttpRequestMessage> BuildRequestFactoryForMethod(

Refit/Attributes.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,12 @@ public QueryAttribute(CollectionFormat collectionFormat)
465465
CollectionFormat = collectionFormat;
466466
}
467467

468+
/// <summary>
469+
/// Used to specify that the value should be treated as a string.
470+
/// Set to true if you want to call ToString() on the object before adding it to the query string.
471+
/// </summary>
472+
public bool TreatAsString { get; set; }
473+
468474
/// <summary>
469475
/// Used to customize the name of either the query parameter pair or of the form field when form encoding.
470476
/// </summary>

Refit/RequestBuilderImplementation.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,18 @@ void AddQueryParameters(RestMethodInfoInternal restMethod, QueryAttribute? query
946946
List<KeyValuePair<string, string?>> queryParamsToAdd, int i, RestMethodParameterInfo? parameterInfo)
947947
{
948948
var attr = queryAttribute ?? DefaultQueryAttribute;
949-
if (DoNotConvertToQueryMap(param))
949+
if (attr is { TreatAsString: true })
950+
{
951+
queryParamsToAdd.AddRange(
952+
ParseQueryParameter(
953+
param.ToString(),
954+
restMethod.ParameterInfoArray[i],
955+
restMethod.QueryParameterMap[i],
956+
attr
957+
)
958+
);
959+
}
960+
else if (DoNotConvertToQueryMap(param))
950961
{
951962
queryParamsToAdd.AddRange(
952963
ParseQueryParameter(

0 commit comments

Comments
 (0)