API REST для обработки данных и цепочки методов

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

Рассмотрим некоторые числовые данные, которые можно получить с помощью вызова GET:

GET http://my.api/data/123/

Пользователи могут применять последовательность арифметических операций, таких как add и multiply. Не-RESTful способ сделать это:

GET http://my.api/data/123?add=10&multiply=5

Assupmtions:

  • Исходные данные в БД не изменены. Только измененная версия возвращается пользователю.
  • Данные большие по размеру (скажем, большой многомерный массив), поэтому мы не можем позволить себе возвращать целые данные при каждом вызове операции. Вместо этого мы хотим применить операции в виде пакета и вернуть окончательные измененные данные в конце.

Есть 2 способа RESTful, которые я сейчас просматриваю:

1. Арифметические операции модели как субресурсы данных.

Если мы рассмотрим add и multiply как подресурсы данных как здесь. В этом случае мы можем использовать:

GET http://my.api/data/123/add/10/

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

GET http://my.api/data/123/add/10/multiply/5/

Где multiply создает подресурс add/10/, который сам является подресурсом data/123

Плюсы:

  • Безгражданство. Разделение не содержит никакой информации об измененных данных.
  • Легкий доступ к измененным данным: это просто простой вызов GET.

Минусы:

  • Цепочка: я не знаю, можно ли ее легко реализовать.
  • Длинные URI: при каждой применяемой операции URI увеличивается дольше.

2. Создайте редактируемый объект данных:

В этом случае пользователь создает редактируемую версию исходных данных:

POST http://my.api/data/123/

вернет

201 Created
Location: http://my.api/data/123/edit/{uniqueid}

Теперь пользователи могут PATCH редактировать данные, доступные для редактирования

PATCH http://my.api/data/123/edit/{uniqueid}
{add:10, multiply:5}

И наконец, GET отредактированные данные

GET http://my.api/data/123/edit/{uniqueid}

Плюсы:

  • Очистить URI.

Минусы:

  • Серверу необходимо сохранить состояние редактируемых данных.
  • Редактирование не является длинным идемпотентом.
  • Для получения отредактированных данных требуется, чтобы пользователи выполняли не менее 3 вызовов.

Есть ли более чистый, более семантический способ выполнения обработки данных RESTfully?

Изменить:

Если вам интересно, что это за реальная проблема, я занимаюсь цифровой обработкой сигналов.

В качестве простого примера вы можете подумать о применении визуальных фильтров к изображениям. Следуя этот пример, веб-служба RESTful может делать:

GET http://my.api/image/123/blur/5px/rotate/90deg/?size=small&format=png

Ответ 1

Несколько вещей, которые стоит рассмотреть в вашем вопросе.

API на основе REST основан на ресурсах

Итак, посмотрим на ваш первый пример, пытаясь связать свойства трансформации с URL-адресом пути после идентификатора ресурса.

GET http://my.api/data/123/add/10/multiply/5/

.. не подходит хорошо (а также сложно реализовать динамически, как вы уже догадались)

Безгражданство

Идея безгражданства в REST построена вокруг одного HTTP-вызова, содержащего достаточно информации для обработки запроса и предоставления результата, не возвращаясь к клиенту для получения дополнительной информации. Сохранение результата HTTP-запроса на сервере не является состоянием, его кешем.


Теперь, учитывая, что API на основе REST, вероятно, не подходит для вашего использования, если вы все еще хотите его использовать, вот ваши варианты:

1. Используйте Querystring с общей операцией URL

Вы можете использовать Querystring, но упростить путь к ресурсу, чтобы принимать все преобразования на одном URI. Учитывая ваши примеры и нежелание хранить преобразованные результаты, это, вероятно, ваш лучший вариант.

GET http://my.api/data/123/transform?add=10&multiply=5

2. Используйте POST не-RESTfully

Вы можете использовать запросы POST и использовать тело HTTP для отправки параметров преобразования. Это гарантирует, что вы никогда не исчерпаете пробел в строке запроса, если вы когда-либо решите сделать много обработки, а также сохранит вашу связь. Это не считается RESTful, если POST возвращает данные изображения.

3. Используйте POST RESTfully

Наконец, если вы решите, что хотите кэшировать вещи, ваш POST может фактически хранить преобразованный объект (обратите внимание, что REST не определяет, как это хранится, в памяти или DB и т.д.), которые могут быть повторно отображены Идентификатор с использованием GET.

Вариант A

POSTing в URI создает подчиненный ресурс.

POST http://my.api/data/123
{add:10, multiply:5}

возвращает

201 Created
Location: http://my.api/data/123/edit/{uniqueid}

затем GET отредактированные данные

GET http://my.api/data/123/edit/{uniqueid}

Вариант B

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

POST http://my.api/data
{original: 123, add:10, multiply:5}

возвращает

201 Created
Location: http://my.api/data/{uniqueid}

затем GET отредактированные данные

GET http://my.api/data/{uniqueid}

Ответ 2

Существует несколько способов сделать это. В конце концов, он должен быть чистым, независимо от того, какой ярлык вы хотите дать (REST non-REST). REST не является протоколом с RFC, поэтому не беспокойтесь о том, передаете ли вы информацию в виде URL-адресов или параметров URL. Подлежащий вебсервису должен быть в состоянии предоставить вам данные без учета того, как они передаются. Например, Java-Джерси предоставит вам свои параметры независимо от того, являются ли они параметром или URL-адресом, его просто отличительной особенностью.

Возвращаясь к вашей конкретной проблеме, я думаю, что ресурс в этом вызове типа REST - это не столько данные, которые используются для выполнения числовых операций, но и фактический ответ. В этом случае POST, где идентификатор данных и операции являются полями, могут быть достаточными.

POST http://my.api/operations/

{
    "dataId": "123",
    "operations": [
        {
            "type": "add",
            "value": 10
        },
        {
            "type": "multiply",
            "value": 5
        }
    ]
}

Отклик должен указывать на местоположение, где результат может быть получен, как вы указали. Результат, на который ссылается местоположение (и ID) в ответе, по существу является неизменным объектом. Таким образом, на самом деле ресурс создается POST, а не данные, используемые для вычисления этого результата. Это просто другой способ его просмотра.

EDIT: в ответ на ваш комментарий о том, что вы не хотите сохранять результат операций, вы можете использовать обратный вызов для передачи результатов операции вызывающему. Вы можете легко добавить поле в вход JSON для хоста или URL-адреса обратного вызова. Если URL-адрес обратного вызова присутствует, вы можете отправить сообщение POST на этот URL с результатами операции.

{
    "dataId": "123",
    "operations": [
        {
            "type": "add",
            "value": 10
        },
        {
            "type": "multiply",
            "value": 5
        }
    ],
    "callBack": "<HOST or URL>"
}

Ответ 3

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

Я много думал об этом. Основная проблема с предлагаемыми в настоящее время архитектурами - это масштабируемость, поскольку сервер создает копии данных каждый раз, когда он работает.

Единственный способ избежать этого - моделировать operations и data отдельно. Итак, как и Хосе, мы создаем ресурс:

POST http://my.api/operations/
{add:10, multiply:5}

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

201 Created
Location: http://my.api/operations/{uniqueid}

Следующий шаг: применить operations к данным:

GET http://my.api/data/123/operations/{uniqueid}

Этот подход к синтаксическому моделированию имеет несколько преимуществ:

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

    POST http://my.api/operations/
    {greyscale: "50%"}
    

    И затем примените эту операцию ко всем моим изображениям:

    GET http://my.api/image/{image_id}/operations/{geyscale_id}
    

    Пока я не хочу изменять набор операций, я могу использовать только GET.

  • Общие операции могут быть созданы и сохранены на сервере, поэтому пользователям их не нужно создавать. Например:

    GET http://my.api/image/{image_id}/operations/flip
    

    Где operations/flip уже доступен доступный набор операций.

  • Легко, применяя один и тот же набор операций к разным данным и наоборот.

    GET http://my.api/data/{id1},{id2}/operations/{some_operation}
    

    Позволяет сравнить два набора данных, которые обрабатываются аналогичным образом. В качестве альтернативы:

    GET http://my.api/data/{id1}/operations/{some_operation},{another_operation}
    

    Позволяет увидеть, как различные результаты обработки влияют на результат.

Ответ 4

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

GET http://my.api/data/123?transform="5*(data+10)"
POST http://my.api/data/123 {"transform": "5*({data}+10)"}

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