REST - комплексные приложения

Я изо всех сил пытаюсь применить принципы RESTful к новому веб-приложению, над которым я работаю. В частности, идея о том, чтобы быть RESTful, каждый HTTP-запрос должен содержать достаточно информации для своего получателя, чтобы обработать его в полной гармонии с безгражданностью HTTP.

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

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

Чтобы быть спокойным, должна ли вся эта информация включаться в каждый запрос? Это, по-видимому, создает огромные накладные расходы в сети. Кроме того, не будут ли ограничения на длину URL, по крайней мере для GET, сделать это невозможным?

Ответ 1

"Фильтровать как ресурс" - идеальный такт для этого.

Вы можете выполнить фильтрацию фильтра в ресурсе фильтра и вернуть идентификатор фильтра.

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

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

GET/лекарства? filter = 1234 & page = 4 & pagesize = 20

Я бы запускал необработанные фильтры через какой-то процесс канонизации, просто чтобы иметь нормализованный набор, так что, например, filter "firstname = Bob lastname = Eubanks" идентичен "lastname = Eubanks firstname = Bob". Это только я, хотя.

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

Изменить ответный вопрос...

Давайте начнем с основ.

Просто вы хотите указать фильтр для использования в запросах, но эти фильтры (потенциально) задействованы и сложны. Если это было просто/лекарства/1234, это не было бы проблемой.

Эффективно, вам всегда нужно отправить фильтр в запрос. Вопрос заключается в том, как представить этот фильтр.

Основная проблема с такими вещами, как сеансы в системах REST, заключается в том, что они обычно управляются "вне диапазона". Когда вы, скажем, идете и создаете лекарство, вы PUT или POST на ресурс медикаментов, и вы получите ссылку на этот препарат.

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

К сожалению, когда вы используете что-то вроде файла cookie, и вам нужен этот файл cookie для вашего запроса, имя ресурса больше не является истинным представлением ресурса. Теперь это имя ресурса (URL) и файл cookie.

Итак, если я делаю GET на ресурсе с именем /medications/search, а cookie представляет сеанс, и на этом сеансе есть фильтр, вы можете увидеть, как это происходит, это имя ресурса/медикаменты/search, на самом деле не очень полезно. У меня нет всей информации, необходимой мне для эффективного использования, из-за побочного эффекта cookie и сеанса и фильтра в нем.

Теперь вы могли бы переписать имя:/medicications/search? session = ABC123, эффективно внедряя файл cookie в имя ресурса.

Но теперь вы столкнулись с типичным контрактом сессий, особенно в том, что они недолговечны. Итак, этот ресурс назван менее полезным, долгосрочным, а не бесполезным, менее полезным. Прямо сейчас этот запрос дает мне интересные данные. Завтра? Возможно нет. Я получу какую-то неприятную ошибку в связи с отсутствием сеанса.

Другая проблема заключается в том, что сеансы обычно не управляются как ресурс. Например, они обычно являются побочным эффектом, vs явно управляется с помощью GET/PUT/DELETE. Сессии также являются "мусорной кучей" состояния веб-приложения. В этом случае мы просто надеемся, что сессия правильно заполнена тем, что необходимо для этого запроса. На самом деле мы этого не знаем. Опять же, это побочный эффект.

Теперь, пусть немного повернуть его на голову. Пусть использование/лекарства/поиск? Filter = ABC123.

Очевидно, что случайно это выглядит одинаково. Мы просто изменили имя с 'session' на 'filter'. Но, как обсуждалось, фильтры в этом случае являются "ресурсом первого класса". Они должны быть созданы, управляться и т.д. Так же, как лекарство, JPEG или любой другой ресурс в вашей системе. Это ключевое различие.

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

Итак, вы можете видеть, как на расстоянии 30 000 футов между фильтром и сеансом нет большой разницы. Но когда вы приближаетесь, они совсем разные.

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

Если бы я это делал, как бы я работал с фильтрами?

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

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

Затем я бы сохранил фильтр как документ XML или JSON, в зависимости от того, что более удобно или подходит для приложения. Я бы дал каждому фильтру уникальный ключ (естественно), но я бы также сохранил хэш для фактического документа с фильтром.

Я бы сделал это, чтобы быстро найти, если фильтр уже сохранен. Поскольку я нормализую это, я "знаю", что XML (скажем) для логически эквивалентных фильтров будет идентичным. Поэтому, когда кто-то переходит к PUT или добавляет новый фильтр, я бы сделал чек на хеш, чтобы узнать, сохранилось ли оно ранее. Я, возможно, вернусь более чем к одному (хеши могут столкнуться, конечно), поэтому мне нужно проверить фактические данные XML, чтобы увидеть, соответствуют ли они.

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

Я также не разрешаю фильтр UPDATE/POST. Поскольку я передаю ссылки на эти фильтры, я бы сделал их неизменными, поэтому ссылки могут оставаться в силе. Если бы я хотел получить фильтр "роль", скажем, "получить весь фильтр истечения срока действия лекарств", тогда я бы создал ресурс "named filter", который связывает имя с экземпляром фильтра, так что фактические данные фильтра могут изменяться, но имя остается неизменным.

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

Надеюсь, это прояснит проблему для вас.

Ответ 2

Чтобы быть спокойным, должна ли вся эта информация включаться в каждый запрос?

Нет. Если это похоже на то, что ваш сервер отправляет (или получает) слишком много информации, скорее всего, есть один или несколько ресурсов, которые вы еще не идентифицировали.

Первым и самым важным шагом в разработке системы RESTful является определение и определение ваших ресурсов. Как вы это сделаете для своей системы?

Из вашего описания здесь можно найти один возможный набор ресурсов:

  • Пользователь - пользователь системы (возможно, врач или пациент (?) - роль может быть представлена ​​в качестве ресурса здесь).
  • Лекарство - вещество в бутылке, но оно также может представлять собой бутылку (количество и содержимое), или она может представлять собой определенную бутылку - в зависимости от того, являетесь ли вы аптекой или просто службой поддержки.
  • Болезнь - условие, для которого пациент может принять лекарство.
  • Пациент - человек, который может принять Лечение
  • Рекомендация - лекарство, которое может быть полезно для пациента, основанного на Болезни, от которой они страдают.

Затем вы можете искать отношения между ресурсами;

  • Пользователь имеет и принадлежит многим ролям
  • Лекарства имеют и принадлежат ко многим заболеваниям.
  • Болезнь имеет много рекомендаций.
  • Пациент имеет и принадлежит многим лекарствам и болезням (бедный человек).
  • У пациента много рекомендаций.
  • Рекомендация имеет одного пациента и имеет одну болезнь

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

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

# view all Recommendations for the patient
GET http://server.com/patients/{patient}/recommendations

# view all Recommendations for a Medication
GET http://servier.com/medications/{medication}/recommendations

# add a new Recommendation for a Patient
PUT http://server.com/patients/{patient}/recommendations

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

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

Ответ 3

REST - это API, а не (типичные) приложения. Не пытайтесь вклинивать фундаментальное взаимодействие с состоянием в модель без состояния, потому что вы читаете об этом в википедии.

Чтобы быть спокойным, должна ли вся эта информация включаться в каждый запрос? Это, по-видимому, создает огромные накладные расходы в сети. Кроме того, не будут ли ограничения на длину URL, по крайней мере для GET, сделать это невозможным?

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

Существенных ограничений по длине URL-адреса нет - если ваш сервер имеет такой предел, обновите его. Вероятно, это было лет, и все равно были уязвимы для безопасности.

Ответ 4

Нет, все это не должно быть в каждом запросе.

Каждый ресурс (лекарство, история болезни и т.д.) должен иметь канонический URI, который однозначно идентифицирует его. В некоторых приложениях (например, на основе Rails) это будет выглядеть как "/patient/1234" или "/drugs/5678", но формат URL не имеет значения.

Клиент, который ранее получил URI для ресурса (например, из поиска или из ссылки, встроенной в другой ресурс), может извлечь его с помощью этого URI.

Ответ 5

Вы работаете над API RESTful, который другие приложения будут использовать для поиска ваших данных? Или вы создаете веб-приложение, ориентированное на конечное пользователя, где пользователи будут входить в систему и выполнять эти поиски?

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

Вот отличный пост в блоге об использовании GET vs POST. В нем указано ограничение длины URL, заданное Internet Explorer объемом 2048 символов, поэтому вы хотите использовать POST для длинных запросов.

http://carsonified.com/blog/dev/the-definitive-guide-to-get-vs-post/