Статьи

Сериализаторы активной модели, Rails и JSON! О, МОЙ!

Концепция нотации объектов JavaScript.

JSON (нотация объектов JavaScript) — это формат, который можно использовать для хранения или обмена данными. Его легко читать людям и анализировать на машинах, поэтому многие API используют JSON.

В этой статье мы узнаем, как создавать собственные ответы JSON с ActiveModel :: Serializer . Все примеры создаются с использованием приложения Ruby on Rails . Создать JSON-ответы в Rails очень просто, но использование фреймворка по умолчанию недостаточно, и его нелегко проверить.

Рассмотрим следующий код:

render json: user

Приведенный выше код создаст ответ JSON, который состоит из всех user Есть ряд опций, которые вы можете использовать с ним, например, includeonlyexceptmethods

В этой статье мы узнаем, как создавать сериализаторы и управлять ими для моделей со следующими отношениями.

Модели-большие

В этой статье мы будем использовать фиктивное приложение, приведенные выше модели будут отображаться как ответ для следующих конечных точек API:

  • Получить последние видео
  • Получить профиль пользователя
  • Получить последние комментарии к видео

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

В этой статье будут обсуждаться только управление и реализация сериализаторов. Здесь не будут обсуждаться другие детали реализации, необходимые для достижения работающего приложения. Окончательное приложение доступно на Github в качестве дополнения к этой статье.

Введение в ActiveModel :: Сериализатор

ActiveModel::SerializerActiveModel::Serializer Имея это в виду, это дает нам лучший способ тестирования по сравнению с другими методами. Его также можно протестировать изолированно, независимо от того, как осуществляется поиск данных в контроллере.

Я использую ActiveModel::Serializer Стратегии, представленные в этой статье, не относятся ни к этой версии, ни к этой жемчужине. Он поддерживается в предыдущей версии и будет поддерживаться в будущей версии. Однако детали реализации могут быть разными для каждой версии, пожалуйста, обратитесь к документации для разных версий.

Установка

Добавьте следующий драгоценный камень в свой Gemfile

 gem 'active_model_serializers', '0.9.3'

Затем установите его, используя пакет:

 bundle install

Вот и все, установка завершена.

использование

Вы можете создать сериализатор следующим образом:

 rails g serializer user

Приведенный выше генератор создаст сериализатор в app / serializers / user_serializer.rb со следующим содержимым:

 # app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
end

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

Ограничения сериализации

Прежде чем углубляться в детали реализации различных стратегий рендеринга пользовательских ответов JSON, мы должны иметь четкое представление о том, как управлять сериализаторами. Очень просто создавать сложные ответы JSON с помощью ActiveModel::Serializer

В следующих примерах используются основные ограничения, которым вы можете следовать. Они должны служить цели простых и поддерживаемых сериализаторов.

модели

Каждая модель должна иметь сопровождающий сериализатор с атрибутами, требуемыми клиентом. В приведенном выше случае использования у нас должны быть следующие сериализаторы:

 # app/serializers/video_serializer.rb
class VideoSerializer < ActiveModel::Serializer
  attributes :id, :title, :description
end

# app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name
end

# app/serializers/comment_serializer.rb
class CommentSerializer < ActiveModel::Serializer
  attributes :id, :text
end

Можно автоматически включить все атрибуты модели, но это создаст риск случайной отправки конфиденциальных данных клиенту. Это также наводняет клиента ненужными данными. Это настоятельно не рекомендуется, если модель не имеет небольшой набор атрибутов, и они редко меняются.

В этом случае приведенный ниже сериализатор (не связанный с нашим вариантом использования, а только используемый в этом примере) служит для простого примера сериализатора, который включает все атрибуты автоматически:

 # app/serializers/tag_serializer.rb
class TagSerializer < ActiveModel::Serializer
  attributes *Tag.column_names
end

Конечные точки

Каждая конечная точка требует нового сериализатора. Эти сериализаторы отличаются от вышеуказанных сериализаторов. Каждая конечная точка имеет выделенный сериализатор, чтобы уменьшить зависимость для данного сериализатора и повысить удобство сопровождения путем введения взаимно-однозначного отношения между конечной точкой и сериализатором.

Вот сериализаторы, требуемые конечными точками в нашем случае использования. Мы можем использовать наследование для обобщения атрибутов между другими сериализаторами данной модели:

 # serializer for API latest videos
# app/serializers/videos/index_serializer.rb
class Videos::IndexSerializer < VideoSerializer
end

# serializer for API user profile
# app/serializers/users/show_serializer.rb
class Users::ShowSerializer < UserSerializer
  root 'user'
end

# serializer for API video's latest comments
# app/serializers/comments/index_serializer.rb
class Comments::IndexSerializer < CommentSerializer
end

В сериализаторе есть интересное утверждение для пользовательского профиля API. Если вы не зададите корневое имя этого сериализатора, оно будет show Если вам не нужен рут, вы можете установить root на false

Встраивание данных

Встраивание данных — это способ включения ссылок или данных, связанных с запрашиваемым объектом. ActiveModel::Serializer Этот метод аналогичен добавлению отношения к модели ActiveRecord. Давайте посмотрим на следующий пример:

 # app/serializers/videos/index_serializer.rb
class Videos::IndexSerializer < VideoSerializer
  has_one :user

  # WARNING:
  # The following is for example purposes only, try to avoid at all costs.
  has_many :comments
end

Хотя это можно сделать выше, это настоятельно не рекомендуется. Вы можете случайно отправить очень большое сообщение с помощью has_many Вместо выполнения описанного выше метода, разделите запрос на две конечные точки, используя два сериализатора. Поэтому у нас будут следующие сериализаторы:

 # app/serializers/videos/index_serializer.rb
class Videos::IndexSerializer < VideoSerializer
  has_one :user
end

# app/serializers/comments/index_serializer.rb
class Comments::IndexSerializer < CommentSerializer
  has_one :user
end

Сериализатор, который требует загрузки большого количества дочерних элементов, должен быть разбит на несколько сериализаторов. Таким образом, это может быть легко поддержано и избежать ненужных рисков случайной отправки огромного количества данных клиенту.

Встраивание и обнаружение ссылок

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

Стратегии ответа JSON для встраивания данных могут быть выполнены с использованием вложенных данных или дополнительной загрузки данных. Стратегия обеспечения обнаружения ссылок может быть реализована с использованием ответов JSON на основе HATEOAS .

Ответ JSON с вложенными данными

Вложенные данные позволяют клиенту загружать все ссылочные данные одновременно, поэтому мы можем уменьшить количество вызовов, необходимых для загрузки всех данных. Эта стратегия не требует обработки данных и может быть немедленно использована для отображения данных. Недостаток этой стратегии заключается в том, что она может вводить дублирование данных. Одни и те же данные могут быть вложены в несколько объектов.

Для этого вам необходимо включить данные, которые должны быть вложены, определив их взаимосвязь. Используя ранее определенные сериализаторы для каждой конечной точки, определите отношения внутри сериализатора:

 # app/serializers/videos/index_serializer.rb
class Videos::IndexSerializer < VideoSerializer
  has_one :user
end

# app/serializers/users/show_serializer.rb
class Users::ShowSerializer < UserSerializer
  root 'user'
end

# app/serializers/comments/index_serializer.rb
class Comments::IndexSerializer < CommentSerializer
  has_one :user
end

Вышеуказанные сериализаторы будут генерировать ответы JSON следующим образом.

Получить последние видео

 {
  "videos":[
    {
      "id":135801055,
      "title":"Les quatre cents coups",
      "description":"Moving story of a young boy who, left without attention, delves into a life of petty crime.",
      "user":{
        "id":321975000,
        "name":"Sarah Ferguson"
      }
    },
    {
      "id":419895383,
      "title":"La heine",
      "description":"24 hours in the lives of three young men in the French suburbs the day after a violent riot.",
      "user":{
        "id":321672063,
        "name":"Javier Dean"
      }
    }
  ]
}

Получить профиль пользователя

 {
  "user":{
    "id":1066742030,
    "name":"Jill Ray"
  }
}

Получить последние комментарии видео

 {
  "comments":[
    {
      "id":632418569,
      "text":"All these jokes are purely jokes - nothing is meant seriously.",
      "user":{
        "id":623761651,
        "name":"Terry Holland"
      }
    }
  ]
}

Ответ JSON с загруженными данными

Дополнительная загрузка данных позволяет клиенту загружать все ссылочные данные, поэтому мы можем сократить количество запросов, необходимых для загрузки всех данных, до одного запроса. Преимущество использования этого подхода заключается в отсутствии дублирования данных в ответе. Недостаток этой стратегии состоит в том, что она требует, чтобы клиент каким-то образом обрабатывал данные перед их отображением.

Чтобы генерировать данные о боковой загрузке, нам нужно сделать то же самое, что мы делали в предыдущем разделе. Затем нам нужно установить его только для встраивания идентификаторов и установки включения в true.

 # app/serializers/videos/index_serializer.rb
class Videos::IndexSerializer < VideoSerializer
  embed :ids, include: true
  has_one :user
end

# app/serializers/users/show_serializer.rb
class Users::ShowSerializer < UserSerializer
  root 'user'
end

# app/serializers/comments/index_serializer.rb
class Comments::IndexSerializer < CommentSerializer
  embed :ids, include: true
  has_one :user
end

Вышеуказанные сериализаторы будут генерировать ответы JSON следующим образом.

Получить последние видео

 {
  "videos":[
    {
      "id":135801055,
      "title":"Les quatre cents coups",
      "description":"Moving story of a young boy who, left without attention, delves into a life of petty crime.",
      "user_id":321975000
    },
    {
      "id":419895383,
      "title":"La heine",
      "description":"24 hours in the lives of three young men in the French suburbs the day after a violent riot.",
      "user_id":321672063
    }
  ],
  "users":[
    {
      "id":321975000,
      "name":"Sarah Ferguson"
    },
    {
      "id":321672063,
      "name":"Javier Dean"
    }
  ]
}

Получить профиль пользователя

 {
  "user":{
    "id":1066742030,
    "name":"Jill Ray"
  }
}

Получить последние комментарии видео

 {
  "comments":[
    {
      "id":632418569,
      "text":"All these jokes are purely jokes - nothing is meant seriously.",
      "user_id":623761651
    }
  ],
  "users":[
    {
      "id":623761651,
      "name":"Terry Holland"
    }
  ]
}

JSON Response на основе HATEOAS

HATEOAS расшифровывается как гипертекст как двигатель состояния приложения. HATEOAS позволяет клиенту полностью взаимодействовать через предоставленный гипермедиа формат сервером. Это означает, что сам гипертекст может использоваться для навигации по API. К сожалению, JSON не является гипермедиа форматом.

Хотя JSON не поддерживает гипермедиа, мы все же можем предоставить способ обнаружения ссылок в JSON. Чтобы обработать ответ JSON на основе HATEOAS, реализуйте обнаружение ссылок в JSON следующим образом:

 # app/serializers/video_serializer.rb
class VideoSerialzer < ActiveModel::Serializer
  attributes :id, :title, :description, :links

  def links
    { self: video_path(object.id) }
  end
end

# app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :links

  def links
    { self: user_path(object.id) }
  end
end

# app/serializers/comment_serializer.rb
class CommentSerializer < ActiveModel::Serializer
  attributes :id, :text, :links

  def links
    { self: comment_path(object.id) }
  end
end

Вышеуказанные сериализаторы будут генерировать ответы JSON следующим образом.

Получить последние видео

 {
  "videos":[
    {
      "id":135801055,
      "title":"Les quatre cents coups",
      "description":"Moving story of a young boy who, left without attention, delves into a life of petty crime.",
      "links":{
        "self":"/videos/135801055"
      },
      "user_id":321975000
    },
    {
      "id":419895383,
      "title":"La heine",
      "description":"24 hours in the lives of three young men in the French suburbs the day after a violent riot.",
      "links":{
        "self":"/videos/419895383"
      },
      "user_id":321672063
    }
  ],
  "users":[
    {
      "id":321975000,
      "name":"Sarah Ferguson",
      "links":{
        "self":"/users/321975000"
      }
    },
    {
      "id":321672063,
      "name":"Javier Dean",
      "links":{
        "self":"/users/321672063"
      }
    }
  ]
}

Получить профиль пользователя

 {
  "user":{
    "id":1066742030,
    "name":"Jill Ray",
    "links":{
      "self":"/users/1066742030"
    }
  }
}

Получить последние комментарии к видео

 {
  "comments":[
    {
      "id":632418569,
      "text":"All these jokes are purely jokes - nothing is meant seriously.",
      "links":{
        "self":"/comments/632418569"
      },
      "user_id":623761651
    }
  ],
  "users":[
    {
      "id":623761651,
      "name":"Terry Holland",
      "links":{
        "self":"/users/623761651"
      }
    }
  ]
}

Преимущество использования этого подхода состоит в том, что клиент может легко взаимодействовать с API, используя ссылки в предоставленном ответе. Но недостатком этого подхода является то, что не существует стандарта для ответов JSON на основе HATEOAS, и, следовательно, клиентская реализация тесно связана с форматом ответа.

Существуют альтернативы форматам JSON на основе HATEOAS, такие как JSON-LD и JSON API . Они предоставляют такие функции, как обнаружение ссылок, но требуют совершенно другой реализации на клиенте.

Вывод

Визуализация пользовательского ответа JSON с использованием ActiveModel::Serializer Неспособность придерживаться этих целей может снизить удобство сопровождения приложений и производительность клиентов. Эта статья должна дать вам базовое представление о том, как отображать пользовательские ответы JSON, используя различные стратегии, которые вам подходят.

Альтернативы, такие как Jbuilder , Grape и RABL, имеют разные подходы к отображению ответа. Чтобы лучше понять, почему вы должны использовать ActiveRecord::Serializer