Как правильно обработать Refit возвращаемые значения? - PullRequest
0 голосов
/ 14 января 2019

Я пишу некоторые API, используя Refit , который творит чудеса, и у меня возникают проблемы с поиском хорошего (как в «чистом», «правильном») способа выполнения некоторой произвольной обработки на возвращенные данные.

В качестве примера рассмотрим этот код:

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> эквивалент, который вызывается в результате выполнения данного API Refit и вносит некоторые изменения в возвращенные данные перед тем, как представить их пользователю.

Ответы [ 2 ]

0 голосов
/ 17 марта 2019

Я обнаружил, что достаточно чистым решением этой проблемы является использование методов расширения для расширения сервисов 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);
}

Я могу просто определить такой метод расширения и использовать его вместо API, предоставляемого службой Refit:

#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, я фактически использую метод расширения, который может позаботиться обо всей дополнительной десериализации для меня.

0 голосов
/ 23 января 2019

Резюме

Вы можете передать пользовательские 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
        }
    )});

Вот базовый пример пользовательского JsonConverter из JSON.Net docs .

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 для вложенной структуры данных, которую вы хотите десериализовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...