我正在使用 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 时,我实际上都在使用可以为我处理所有额外反序列化的扩展方法。