Looking forwards to RestEase 2
See original GitHub issueAt some point, I’d like to move to RestEase 2. There are two main things want to achieve:
- Make use of source generators
- Make Json.NET optional
This issue is a brain dump, and a place to record my thoughts as they evolve.
Source Generators
Source generators are coming. They will essentially let NuGet packages hook into the build system, and insert their own code. This is a perfect fit for something like RestEase, where we can generate the implementation of a user’s interface at compile-time instead of at runtime.
However, I don’t want to remove support for the current IL emission approach.
As I understand it, the options available are:
- Let the user define partial classes with partial methods instead of interfaces, and generate the other part of the partial class.
- Keep the current approach of letting the user define an interface, and generate an implementation for that.
Partial classes have some appeal: they would let users write:
partial class MyApi
{
[Get("foo")]
partial Task FooAsync();
}
var api = new MyApi("https://api.example.com");
which avoids needing to use reflection. It also means the user can add their own concrete helper methods. However, there are some downsides: users like being able to define interfaces from an abstraction standpoint, and it would make the README significantly more complex (each code sample needs duplicating).
Continuing to use interfaces shouldn’t be too bad: we can emit an assembly-level attribute saying which generated type implements which interface, which we can pick up on at runtime.
Another consideration is what will happen if the source generator isn’t working. With a partial class, the compilation will fail. With interfaces, we simply won’t get an implementation generated, and we won’t know until runtime. I don’t know whether this might be an issue in practice.
We’d also want to keep the RestClient.For
syntax for interfaces, presumably. However, I’d want both source generation and runtime IL generation to be opt-in, and allow both to run side-by-side, so RestClient
would need to be defined in a central place, and do nasty things to make sure that it did runtime generation if necessary, and if possible. That could cause problems.
Identifying which partial classes / interfaces need implementations generated could be tricky: we don’t currently have a guaranteed type-level attribute, so we’d have to go looking through all methods of all types.
Json.NET
Json.NET is currently a mandatory dependency. That makes the simple case simpler, but means people who don’t want to use it still have to take a pointless dependency.
This never used to be much of a problem: most REST APIs are json, and everyone used Json.NET. However System.Text.Json is now a thing, and forcing people who want to use System.Text.Json to take a dependency on Json.NET is wrong.
However, we still need to support people who want to use Json.NET.
Backwards compatibility
Major version bumps are a good excuse to make breaking changes. I’m OK with breaking changes in principle, but they must be compile-time breakages (I don’t want things failing only at runtime), and there should ideally be guidance on how to solve it (e.g. use of Obsolete attributes).
Changes
I want to move all common types (attributes, RequestInfo
, Requester
, etc) to a RestEase.Core
assembly. This is necessary to let source generation and runtime IL generation work side-by-side. These would stay in their current namespaces. It’s unclear what would happen to RestClient
.
Currently the various serializers and deserializers are separate properties on RestClient
. In order to let the user opt into the Json.NET (de)serializers easily, these would have to be grouped on a new object. Then the user could do new RestClient() { Serializers = new JsonNetSerializers() }
or similar. The JsonSerializerSettings
would have to be removed from RestClient
as well.
We’d need a RestEase.Json.NET
NuGet package which contains the Json.NET (de)serializers.
Presumably we’d want two more NuGet packages, one for the source generators and one for the runtime IL emission (they can’t be the same package, because the source generators shouldn’t have a dependency on ILBuilder).
It’s unclear what should happen to the current RestEase
NuGet package. Maybe this should continue to contain the IL emission code, or maybe it should be a backwards-compatibility meta-package which references the IL emission NuGet package, as well as the RestEase.Json.NET
NuGet package.
Other Changes
It’s also a chance to tackle #19, and not assume that responses are always strings. This needs more thought.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:12
- Comments:15 (10 by maintainers)
Top GitHub Comments
I’ve been mulling over this further, and I’m leaning in favour of keeping things as a single package. This would include the source analyzer and the S.R.E implementation. The S.R.E would be used as a fallback in case the source generator fails, or isn’t available (on platforms which support it).
This makes life a lot simpler: people don’t have to make informed decisions at the point of installing the library (which is a hurdle). They install the RestEase package, and if their toolchain supports source generators, they get nice compile-time error messages; if it doesn’t, things work as before (except on Xamarin.iOS, where they’ll get a nice exception message pointing them at the docs for source generators).
This does mean we’ll ship a S.R.E implementation to platforms which don’t support it, but that’s probably OK. .NET Standard 2.1 includes S.R.E (and the latest Xamarin versions target .NET Standard 2.1), so we won’t have an extraneous dependency on the S.R.E NuGet package
There is the thorny issue of what to do about the Newtonsoft.Json dependency, though. Intuitively, we should target System.Text.Json by default, but 1) that’s only supported on some platforms, so we’ll get different behaviour depending on the platform, and 2) that ends up going down the route of silently switching people from Newtonsoft.Json to System.Text.Json, which is going to break things for some people. Therefore I think we have the following options:
SerializationMethod
, etc), and throw a suitably informative error message. We will break some users this way, however.You can write your own serializer which uses STJ - the only side is you’ll bring in json.net as a dependency, even though it’s unused.
There’s also a third party fork of RestEase on nuget, although I can’t vouch for it