Обнаружение API REST и HATEOAS

Обнаружение API REST и HATEOAS

1. обзор

Эта статья будет посвященаDiscoverability of the REST API, HATEOAS и практическим сценариям, основанным на тестах.

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

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

Чтобы понять возможность обнаружения, нам нужно понять ограничение Hypermedia As Engine of Application State (HATEOAS). This constraint of a REST API is about full discoverability of actions/transitions on a Resource from Hypermedia (Hypertext really), as the only driver of application state.с

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

В заключение,the server should be descriptive enough to instruct the client how to use the API только через гипертекст. В случае HTTP-диалога мы могли бы добиться этого с помощью заголовкаLink.

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

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

В этом разделе мы будем тестировать отдельные признаки обнаруживаемости с помощью Junit,rest-assured иHamcrest. Начиная сthe REST Service has been previously secured, каждый тест сначала должен выполнитьauthenticate перед использованием API.

3.1. Откройте для себя действующие методы HTTP

Когда служба REST используется с недопустимым методом HTTP, ответ должен быть 405 МЕТОД НЕ РАЗРЕШЕН.

API также должен помогать клиенту находить действительные методы HTTP, которые разрешены для этого конкретного ресурса. Для этогоwe can use the Allow HTTP Header in the response:

@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 вновь созданного ресурса

The operation of creating a new Resource should always include the URI of the newly created resource in the response. Для этого мы можем использовать 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));
}

Тест проводится по простому сценарию:creating a new Foo resource, then using the HTTP response to discover the URI where the Resource is now available. Затем он также выполняет GET для этого URI, чтобы извлечь ресурс и сравнить его с оригиналом. Это делается для того, чтобы убедиться, что он был правильно сохранен.

3.3. Найдите URI, чтобы получить все ресурсы этого типа

When we GET any particular Foo resource, we should be able to discover what we can do next: we can list all the available Foo resources. Таким образом, операция получения ресурса всегда должна включать в свой ответ 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 по отношениюrelis shown here.

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

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

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

Other URIs could potentially be discovered via the Link header, но существующие типы отношений ссылок позволяют не переходить к более богатой семантической разметке, такой какdefining custom link relations,Atom Publishing Protocol илиmicroformats, которые будут тема другой статьи.

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

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

5. Заключение

Мы виделиhow a REST API is fully discoverable from the root and with no prior knowledge - это означает, что клиент может перемещаться по нему, выполняя GET в корне. В дальнейшем все изменения состояния управляются клиентом с использованием доступных и обнаруживаемых переходов, которые REST API предоставляет в представлениях (отсюдаRepresentational State Transfer).

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

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