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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
5 534
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Резюме

Вы можете передать настраиваемый 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.

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

Привет, спасибо за ответ, но на самом деле это не ответ на мой вопрос. Как я показал в своем примере, мне не нужно настраивать работу десериализации по умолчанию, но нужно выполнять дополнительную обработку десериализованных данных вместо того, чтобы напрямую возвращать их вызывающей стороне. Или выполнить настраиваемую десериализацию для типов возвращаемых значений, которые являются переменными (например, массив JSON с объектами разных типов). Ни один из этих двух случаев не может быть легко обработан с помощью специального десериализатора.

Sergio0694 17.03.2019 15:21

Спасибо за отзыв и публикацию найденного решения! Я поддержал ваш ответ в надежде, что в будущем он появится раньше моего.

jeyoor 25.03.2019 00:58
Ответ принят как подходящий

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

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

Другие вопросы по теме