5

I'm writing some APIs using Refit, which works wonders, and I'm having some trouble figuring out a good (as in "clean", "proper") way to perform some arbitrary processing on the returned data.

As an example, consider this code:

public interface ISomeService
{
    [Get("/someurl/{thing}.json")]
    Task<Data> GetThingAsync([AliasAs("thing")] string thing);
}

Now, a lot of REST APIs I've seen have the unfortunate habit of packing the actual data (as in "useful" data) deep into the JSON response. Say, the actual JSON has this structure:

{
    "a" = {
        "b" = {
            "data" = {
...
}

Now, typically I'd just map all the necessary models, which would allow Refit to correctly deserialize the response. This though makes the API a bit clunky to use, as every time I use it I have to do something like:

var response = await SomeService.GetThingAsync("foo");
var data = response.A.B.Data;

What I'm saying is that those two outer models are really just containers, that don't need to be exposed to the user. Or, say the Data property is a model that has another property that is an IEnumerable, I might very well just want to directly return that to the user.

I have no idea on how to do this without having to write useless wrapper classes for each service, where each one would also have to obviously repeat all the XML comments in the interfaces etc., resulting in even more useless code floating around.

I'd just like to have some simple, optional Func<T, TResult> equivalent that gets called on the result of a given Refit API, and does some modifications on the returned data before presenting it to the user.

4

2 回答 2

6

我发现这个问题的一个足够干净的解决方案是使用扩展方法来扩展 Refit 服务。例如,假设我有一个这样的 JSON 映射:

public class Response
{
    [JsonProperty("container")]
    public DataContainer Container { get; set; }
}

public class DataContainer
{
    [JsonProperty("data")]
    public Data Data { get; set; }
}

public class Data
{
    [JsonProperty("ids")]
    public IList<string> Ids { get; set; }
}

然后我有一个这样的改装 API:

public interface ISomeService
{
    [Get("/someurl/{thing}.json")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("use extension " + nameof(ISomeService) + "." + nameof(SomeServiceExtensions.GetThingAsync))]
    Task<Response> _GetThingAsync(string thing);
}

我可以像这样定义一个扩展方法,并使用这个来代替 Refit 服务公开的 API:

#pragma warning disable 612, 618

public static class SomeServiceExtensions
{
    public static Task<Data> GetThingAsync(this ISomeService service, string thing)
    {
        var response = await service._GetThingAsync(thing);
        return response.Container.Data.Ids;
    }
}

这样,每当我调用GetThingAsyncAPI 时,我实际上是在使用可以为我处理所有额外反序列化的扩展方法。

于 2019-03-17T14:29:26.967 回答
3

概括

您可以将自定义JsonConverters传递给 Refit 以修改它如何序列化或反序列化各种类型。

细节

RefitSettings类提供自定义选项,包括 JSON 序列化程序设置

请注意,在过去的几个版本中,RefitSettings 类发生了一些变化。您应该查阅适用于您的 Refit 版本的文档。

来自 Refit 的最新示例

var myConverters = new List<JsonConverter>();
myConverters += new myCustomADotBConverter();

var myApi = RestService.For<IMyApi>("https://api.example.com",
    new RefitSettings {
        ContentSerializer = new JsonContentSerializer( 
            new JsonSerializerSettings {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                Converters = myConverters
        }
    )});

这是来自JSON.Net 文档的自定义 JsonConverter 的基本示例。

public class VersionConverter : JsonConverter<Version>
{
    public override void WriteJson(JsonWriter writer, Version value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString());
    }

    public override Version ReadJson(JsonReader reader, Type objectType, Version existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        string s = (string)reader.Value;

        return new Version(s);
    }
}

public class NuGetPackage
{
    public string PackageId { get; set; }
    public Version Version { get; set; }
}

该示例 JsonConverter 旨在序列化或反序列化 JSON 有效负载的“版本”字段,如下所示:

{
  "PackageId": "Newtonsoft.Json",
  "Version": "10.0.4"
}

您必须为要反序列化的嵌套数据结构编写自己的自定义 JsonConverter。

于 2019-01-23T16:04:11.217 回答