I currently have two applications, which communicate with each other using a REST API. The first is a .NET 7 Web API project and the second is a Java desktop app which uses an HTTP Client to connect to the API.
In the current implementation I am forced to write all of the REST models as both Java classes and C# classes, which is a little cumbersome.
I have done some looking into Protobufs, and I believe that I can leverage its compiler to do this class creation for me. Instead of writing language specific classes I would instead write a .proto
file and compile it to create the classes. It's worth reiterating that neither of the applications use gRPC, just REST.
One file in my solution allows a user to search for events within a specified date range:
message SearchParameters {
google.protobuf.Timestamp date_range_start = 1;
google.protobuf.Timestamp date_range_end = 2;
}
Previously this worked by passing two dates to the API in RFC 3339 format (e.g. 2020-12-09T16:09:53Z
), however now that I have introduced Protobufs my API is expecting the dates to be in the format:
{
"seconds": 11223344,
"nanos": 0
}
I would like to continue to pass dates in as RFC 3339 strings, as opposed to to seconds
/nanos
.
I have tried to add a JsonConverter
to the C# project, but it had some issues with the dates getting wrapped in an extra set of quotes (i.e. "\"2020-12-09T16:09:53Z\""
), and it only "fixed" the return types, if I passed dates in RFC 3339 format they would end up as null
.
Additionally there are indications in the Protobufs documentation that the expected behaviour is for RFC 3339 format to be used when mapping to JSON:
In JSON format, the Timestamp type is encoded as a string in the RFC 3339 format.
Refs:
https://cloud.google.com/ruby/docs/reference/google-cloud-dlp-v2/latest/Google-Protobuf-Timestamp
https://protobuf.dev/reference/java/api-docs/com/google/protobuf/Timestamp
https://protobuf.dev/reference/php/api-docs/Google/Protobuf/Timestamp
Because of this I am not really sure that writing a JsonConverter
is the correct approach.
Is there some way to make Protobuf Timestampss work like I want, or is there perhaps an alternative to either Timestamps, or Protobuf itself that would solve the issue of sharing REST API models between languages
In the end I was able to solve my issue by creating a TypeConverter
, to convert the RFC 3339 string to a Timestamp
.
Using a TypeConverter
was much more versatile than a custom Model Binder, as it only needed to be defined once, rather than manually specifying the custom model binder any time I used a Timestamp
in my API.
TypeDescriptor.AddAttributes(typeof(Timestamp), new TypeConverterAttribute(typeof(TimestampTypeConverter)));
public class TimestampTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, System.Type sourceType)
{
return sourceType == typeof(string);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
return Timestamp.FromDateTime(DateTime.Parse(value.ToString()!, null, DateTimeStyles.RoundtripKind));
}
}