1

I have an ASP .NET 6 Rest service in which I have a pretty basic Refit API interface defined for a GET operation with a request object that has several properties on it. Here's the request definition:

[Get("/users")]
Task<Auth0GetUsersResponse> GetUsers(Auth0GetUsersRequest request, [Header("Authorization")] string bearerToken);

The problem is that when Refit serializes the request object and constructs the URL parameters, it generates Boolean values with capital first letters, e.g. True or False. Unfortunately, URLs are case-sensitive and these values are not valid for Boolean inputs, so the API I am calling returns a 400. Here's the GET that is being generated:

GET /api/v2/users?include_totals=True

That value "True" is where I'm dying. Here's the property definition on the Auth0GetUsersRequest object:

public bool include_totals { get; set; }

And here's how I am registering the service in Startup.cs:

services.AddRefitClient<IAuth0ManagementApi>();

This is all really basic stuff. I'm not doing any extra configuring of any kind that I know of.

I've spent most of today trying to figure out ways to make that be "true" instead of "True", switching between System.Text.Json and Newtonsoft frameworks, changing property case convention in the client settings (which shouldn't have any impact but I was grasping at straws), even using a custom JsonConverter. Nothing affects it - Refit just insists on making bool values start with uppercase letters!

This only seems to be happening when serializing URL bool parameters in a GET. When serializing a POST body it works as expected, with properly lowercase values for bools.

Lordshmee
  • 43
  • 3
  • `these values are not valid for boolean inputs` - [they are](https://stackoverflow.com/a/491355/11683), unless the side receiving them arbitrarily [decides](https://stackoverflow.com/a/24700171/11683) otherwise. – GSerg Jan 20 '23 at 18:33
  • I agree with you generally speaking, unfortunately the Web is the Wild West and the owner of the API gets to decide what is valid and what is not. I must comply or not use their API. and I do have to use it. I designed a workaround that I will post shortly. – Lordshmee Jan 20 '23 at 18:59

2 Answers2

2

You could override this behavior:

public sealed class BooleanUrlParameterFormatter : DefaultUrlParameterFormatter
{
    public override string? Format(object? parameterValue, ICustomAttributeProvider attributeProvider, Type type)
    {
        if (parameterValue is bool)
        {
            return parameterValue.ToString()!.ToLower();
        }

        return base.Format(parameterValue, attributeProvider, type);
    }
}

var refitSettings = new RefitSettings
{
    UrlParameterFormatter = new BooleanUrlParameterFormatter()
};
services.AddRefitClient(typeof(IApi), refitSettings);
Ateist
  • 137
  • 2
  • 11
0

I came up with a workaround for this issue, although I would still like to find out if it's possible to use the type I really want to use and have it generate the desired output. Until then though, this is what I did.

I created this Enum:

public class Auth0Enums
{
    public enum UrlBoolean
    {
        [EnumMember(Value = "true")]
        True,

        [EnumMember(Value = "false")]
        False,

        [EnumMember(Value = null)]
        Null
    }
}

And am using it as a stand-in for the type bool. Here is the property definition on my request object now:

public Auth0Enums.UrlBoolean include_totals { get; set; }

Refit uses the EnumMember attribute to output the desired text:

GET /api/v2/users?include_totals=true
Lordshmee
  • 43
  • 3