Перейти к основному содержимому

Возможность обнаружения REST API и HATEOAS

· 5 мин. чтения

1. Обзор

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

2. Зачем делать API доступным для обнаружения

Обнаруживаемость API — это тема, которой не уделяется должного внимания. Как следствие, очень немногие API понимают это правильно. Это также то, что, если все сделано правильно, может сделать API не только RESTful и удобным, но и элегантным.

Чтобы понять возможность обнаружения, нам нужно понять ограничение Hypermedia As The Engine Of Application State (HATEOAS). Это ограничение REST API связано с полной возможностью обнаружения действий/переходов на ресурсе из гипермедиа (фактически гипертекста) как единственного драйвера состояния приложения.

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

В заключение, сервер должен быть достаточно описательным, чтобы проинструктировать клиента, как использовать API только через гипертекст. В случае HTTP-разговора мы могли бы добиться этого с помощью заголовка Link .

3. Сценарии обнаруживаемости (управляемые тестами)

Так что же означает, что служба REST может быть обнаружена?

В этом разделе мы будем тестировать отдельные черты обнаруживаемости с помощью Junit, Rest - Assure и Hamcrest . Поскольку служба REST ранее была защищена , каждый тест должен сначала пройти аутентификацию , прежде чем использовать API.

3.1. Откройте для себя действительные методы HTTP

Когда служба REST используется с недопустимым методом HTTP, ответ должен быть 405 METHOD NOT ALLOWED.

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

@Test
public void
whenInvalidPOSTIsSentToValidURIOfResource_thenAllowHeaderListsTheAllowedActions(){
// Given
String uriOfExistingResource = restTemplate.createResource();

// When
Response res = givenAuth().post(uriOfExistingResource);

// Then
String allowHeader = res.getHeader(HttpHeaders.ALLOW);
assertThat( allowHeader, AnyOf.anyOf(
containsString("GET"), containsString("PUT"), containsString("DELETE") ) );
}

3.2. Откройте для себя URI недавно созданного ресурса

Операция создания нового ресурса всегда должна включать в ответ URI вновь созданного ресурса. Для этого мы можем использовать HTTP-заголовок Location .

Теперь, если клиент выполняет GET для этого URI, ресурс должен быть доступен:

@Test
public void whenResourceIsCreated_thenUriOfTheNewlyCreatedResourceIsDiscoverable() {
// When
Foo newResource = new Foo(randomAlphabetic(6));
Response createResp = givenAuth().contentType("application/json")
.body(unpersistedResource).post(getFooURL());
String uriOfNewResource= createResp.getHeader(HttpHeaders.LOCATION);

// Then
Response response = givenAuth().header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.get(uriOfNewResource);

Foo resourceFromServer = response.body().as(Foo.class);
assertThat(newResource, equalTo(resourceFromServer));
}

Тест проводится по простому сценарию: создание нового ресурса Foo , затем использование ответа HTTP для обнаружения URI, по которому теперь доступен ресурс . Затем он также выполняет GET для этого URI, чтобы получить ресурс и сравнить его с оригиналом. Это нужно для того, чтобы убедиться, что он был правильно сохранен.

3.3. Узнайте URI, чтобы ПОЛУЧИТЬ все ресурсы этого типа

Когда мы ПОЛУЧАЕМ любой конкретный ресурс Foo , мы должны быть в состоянии узнать, что мы можем делать дальше: мы можем перечислить все доступные ресурсы Foo . Таким образом, операция извлечения ресурса всегда должна включать в свой ответ URI, где можно получить все ресурсы этого типа.

Для этого мы снова можем использовать заголовок Link :

@Test
public void whenResourceIsRetrieved_thenUriToGetAllResourcesIsDiscoverable() {
// Given
String uriOfExistingResource = createAsUri();

// When
Response getResponse = givenAuth().get(uriOfExistingResource);

// Then
String uriToAllResources = HTTPLinkHeaderUtil
.extractURIByRel(getResponse.getHeader("Link"), "collection");

Response getAllResponse = givenAuth().get(uriToAllResources);
assertThat(getAllResponse.getStatusCode(), is(200));
}

Обратите внимание, что здесь показан полный низкоуровневый код для extractURIByRel, отвечающего за извлечение URI по отношению rel .

Этот тест охватывает сложную тему отношений ссылок в REST: URI для извлечения всех ресурсов использует семантику rel="коллекция" .

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

4. Другие потенциальные обнаруживаемые URI и микроформаты

Другие URI потенциально могут быть обнаружены с помощью заголовка Link , но существующие типы отношений ссылок не позволяют переходить к более богатой семантической разметке , такой как определение пользовательских отношений ссылок , протокола Atom Publishing Protocol или микроформатов , о которых пойдет речь . другой статьи.

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

К счастью, стандартная практика состоит в том, что URI для создания совпадает с URI для GET всех ресурсов этого типа, с той лишь разницей, что используется HTTP-метод POST.

5. Вывод

Мы видели , как REST API полностью обнаруживается из корня и без каких-либо предварительных знаний — это означает, что клиент может перемещаться по нему, выполняя GET в корне. В дальнейшем все изменения состояния управляются клиентом с использованием доступных и обнаруживаемых переходов, которые REST API предоставляет в представлениях (отсюда Representational State Transfer ).

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

Реализация всех этих примеров и фрагментов кода доступна на GitHub . Это проект на основе Maven, поэтому его легко импортировать и запускать как есть.