我正在使用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
的模型,我可能只是想直接将其返回给用户。
我不知道如何在不必为每个服务编写无用的包装类的情况下执行此操作,其中每个服务器也必须显然重复接口中的所有XML注释等,从而导致更多无用的代码浮动。
我只想拥有一些简单的,可选的Func<T, TResult>
等价物,它可以在给定的Refit API的结果上调用,并在将返回的数据呈现给用户之前对其进行一些修改。
您可以将自定义JsonConverters传递给Refit,以修改它序列化或反序列化各种类型的方式。
RefitSettings类提供自定义选项,包括JSON序列化程序设置。
请注意,在过去的几个版本中,RefitSettings类已经有所改变。您应该查阅适用于您的Refit版本的相应文档。
来自改装的最新examples
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 docs的自定义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有效负载的“Version”字段,如下所示:
{
"PackageId": "Newtonsoft.Json",
"Version": "10.0.4"
}
您必须为要反序列化的嵌套数据结构编写自己的自定义JsonConverter。
我发现这个问题的一个足够干净的解决方案是使用扩展方法来扩展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;
}
}
这样,每当我调用GetThingAsync
API时,我实际上使用的扩展方法可以为我处理所有额外的反序列化。