3

I'm building an API Gateway that got an endpoint that takes DateTime value as a parameter. It forwards this query to an underlying microservice using Refit.

My problem is that when the URL for the query to the microservice is built, the DateTime values have lost precision.

Is there a way to configure Refit to use a custom DateTime serializer when building URLs?

The microservice endpoint is defined like this:

[Get("/client-sales")]
Task<ClientSaleCollectionR12n> SearchClientSales([Header("Authorization")] string authorization,
                                                         DateTime? completedAfter = null,
                                                         DateTime? completedBefore = null);

The query sent to the gateway:

GET /client-sales?completedAfter=2020-03-20T14:54:26.786&completedBefore=2020-03-21T15:16:33.212

Becomes this when forwarded to the underlying microservice:

GET /client-sales?completedAfter=03%2F20%2F2020%2014%3A54%3A26&completedBefore=03%2F21%2F2020%2015%3A16%3A33
Fred
  • 3,365
  • 4
  • 36
  • 57
Øystein
  • 1,163
  • 1
  • 12
  • 23
  • 1
    https://github.com/reactiveui/refit/pull/420: QueryAttribute now has a Format property that works for both FormUrlEncoded values and for url encoded values. – Oguz Ozgul Mar 23 '20 at 09:14
  • Yes after updating refit to the newest version, I was able to use [Query(Format="yyyy-MM-ddThh:HH:ss.fff")] – Øystein Mar 23 '20 at 13:10

3 Answers3

1

I was able to specify the format when defining the Interface.

Task<ClientSaleCollectionR12n> SearchClientSales([Header("Authorization")] string authorization,
                                                         [Query(Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")] DateTime? completedAfter = null,
                                                         [Query(Format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")] DateTime? completedBefore = null);
Øystein
  • 1,163
  • 1
  • 12
  • 23
0

Since refit uses Newtonsoft.Json, maybe setting the DateTimeZoneHandling on the JsonConverter that refit uses works?

https://www.newtonsoft.com/json/help/html/SerializeDateTimeZoneHandling.htm

I came across this since we had the same question, and didn't get a chance yet to try it. but this would be a more universal (/better) approach, if you don't have to specify the format everywhere.

SwissCoder
  • 2,514
  • 4
  • 28
  • 40
0

2023 version 6.3.2 (probably some earlier too) it is possible create custom url converter without using format in interfaces Query attribute Set your own url formater

var refitSettings = new RefitSettings
{
    UrlParameterFormatter = new UrlParameterFormatter()
};
public class UrlParameterFormatter : IUrlParameterFormatter
{
    static readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, EnumMemberAttribute?>> EnumMemberCache = new();

    public virtual string? Format(object? parameterValue, ICustomAttributeProvider attributeProvider, Type type)
    {
        if (attributeProvider is null)
            throw new ArgumentNullException(nameof(attributeProvider));

        // See if we have a format
        var formatString = attributeProvider.GetCustomAttributes(typeof(QueryAttribute), true)
            .OfType<QueryAttribute>()
            .FirstOrDefault()?.Format;

        EnumMemberAttribute? enummember = null;
        if (parameterValue != null)
        {
            var parameterType = parameterValue.GetType();
            if (parameterType.IsEnum)
            {
                var cached = EnumMemberCache.GetOrAdd(parameterType, t => new ConcurrentDictionary<string, EnumMemberAttribute?>());
                enummember = cached.GetOrAdd(parameterValue.ToString()!, val => parameterType.GetMember(val).First().GetCustomAttribute<EnumMemberAttribute>());
            }

            if (parameterType == typeof(DateTime?) || parameterType == typeof(DateTime))
            {
                if (parameterValue is DateTime dateTime)
                {
                    if (dateTime.Kind == DateTimeKind.Local)
                    {
                        dateTime = dateTime.ToUniversalTime();
                    }

                    return dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ");
                }
            }
        }

        return parameterValue == null
            ? null
            : string.Format(CultureInfo.InvariantCulture,
                string.IsNullOrWhiteSpace(formatString)
                    ? "{0}"
                    : $"{{0:{formatString}}}",
                enummember?.Value ?? parameterValue);
    }
}

valentasm
  • 2,137
  • 23
  • 24