我正在使用 Refit 编写一些 API,这会产生奇迹,但我在找出一种好的(如“干净”、“正确”)方式对返回的数据执行一些任意处理时遇到了一些麻烦。

例如,请考虑以下代码:

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

现在,我见过的很多 REST API 都有将实际数据(如“有用”数据)深入到 JSON 响应中的不幸习惯。说,实际的 JSON 具有以下结构:
{
    "a" = {
        "b" = {
            "data" = {
...
}

现在,通常我只会映射所有必要的模型,这将允许 Refit 正确反序列化响应。这虽然使 API 使用起来有点笨拙,因为每次我使用它时,我都必须执行以下操作:

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

我要说的是,这两个外部模型实际上只是容器,不需要向用户公开。或者,假设 Data 属性是一个具有另一个属性 IEnumerable 的模型,我很可能只想直接将其返回给用户。

我不知道如何做到这一点,而不必为每个服务编写无用的包装类,其中每个服务还必须明显重复接口(interface)等中的所有 XML 注释,从而导致更多无用的代码四处飘散。

我只想有一些简单的、可选的 Func<T, TResult> 等效项,它在给定 Refit API 的结果上调用,并在将其呈现给用户之前对返回的数据进行一些修改。

最佳答案

我发现这个问题的一个足够干净的解决方案是使用扩展方法来扩展 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; }
}

然后我有一个像这样的 Refit 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;
    }
}

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

10-07 22:08