Отключить язык гипертекстовых приложений (HAL) в JSON?

Использование Spring Data REST с JPA в версии 2.0.2.RELEASE.

Как отключить язык гипертекстовых приложений (HAL) в JSON? http://stateless.co/hal_specification.html

Я уже много чего перепробовал, но безрезультатно. Например, я установил для заголовков Accept и Content-type значение «application/json» вместо «application/hal+json», но я по-прежнему получаю содержимое JSON с гиперссылками.

Например, я хотел бы получить что-то вроде:

{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"sector" : {
     "description" : "Marketing",
     "average profit": 545656665,
     "average employees": 75,
     "average profit per employee": 4556
     }
}

Вместо:

{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"_links" : {
     "self" : {
          "href" : "http://localhost:8080/app/companies/1"
     },
     "sector" : {
          "href" : "http://localhost:8080/app/companies/1/sector"
     }
}
}

Спасибо за вашу помощь.


person jplandrain    schedule 24.04.2014    source источник


Ответы (2)


(Гипер)типы медиа

Настройки по умолчанию для Spring Data REST используют HAL в качестве формата представления гипермедиа по умолчанию, поэтому сервер вернет следующее для заданных заголовков Accept:

  • Нет заголовка -> application/hal+json -> HAL
  • application/hal+json -> application/hal+json -> HAL
  • application/json -> application/json -> HAL (это то, что настраивается по умолчанию)
  • application/x-spring-data-verbose+json -> application/x-spring-data-verbose+json -> специальный формат Spring Data (с использованием links для контейнера ссылок и content в качестве оболочки для элементов коллекции.

Если вы настроите RepositoryRestConfiguration.setDefaultMediaType(…) на формат, отличный от HAL, сервер вернет формат JSON, специфичный для данных Spring, если вы явно не запросите application/hal+json. По общему признанию, параметр конфигурации, вероятно, немного вводит в заблуждение, поэтому я подал DATAREST-294, чтобы улучшить его. . Проблема была решена в версии 2.1 RC1 (Dijkstra) 2014.

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

Встраивание связанных сущностей

Если вы никогда не хотите, чтобы сектора были связаны и всегда были встроены в них, один из вариантов — просто исключить SectorRepository из экспорта в качестве ресурса REST. Вы можете добиться этого, аннотировав интерфейс репозитория с помощью @RepositoryRestResource(exported = false).

Чтобы получить представление, возвращенное в том виде, в котором вы разместили его в нижнем примере, взгляните на функцию projections. представлен в Spring Data REST 2.1 M1. По сути, это позволяет вам создавать дополнительные представления ресурса, которые могут отличаться от представлений по умолчанию, через простой интерфейс.

Вы бы в основном определили интерфейс:

@Projection(name = "foo", types = YourDomainClass.class)
interface Inlined {

  // list all other properties

  Sector getSector();
}

Если вы либо поместите этот интерфейс в (под)пакет вашего доменного класса, либо вручную зарегистрируете его через RepositoryRestConfiguration.projectionConfiguration(), ресурсы, раскрывающие YourDomainClass, примут параметр запроса projection, так что передача foo в этом примере отобразит встроенное представление так, как вы этого хотите.

Этот коммит содержит больше информации об этой функции в целом, в этом коммите определен пример проекции.

person Oliver Drotbohm    schedule 25.04.2014
comment
Как и советовали, я попытался использовать @RepositoryRestResource(exported = false) для встроенных секторов. Однако в этом случае я получаю JSON с "sector" : null Итак, я смотрю на Spring Data REST 2.1 M1 и вариант прогнозов. - person jplandrain; 25.04.2014
comment
Описание, данное в билете DATAREST-221, на который вы меня отправляете, точно соответствует ситуации, которую я сталкиваюсь. - person jplandrain; 25.04.2014
comment
Мне потребовалось немного времени, чтобы понять все детали, но это работает. Я делал две ошибки: 1. Я не создал класс Projection в своем пакете сущностей (или подпакете сущностей). Вы сказали мне сделать это, но я не заметил 2. Мне нужно создать интерфейс SectorSomething и вернуть его в класс Projection (а не в класс Sector, как написано в вашем ответе). Большое спасибо за Вашу помощь. - person jplandrain; 28.04.2014
comment
Небольшое замечание (в моем случае это не баг и даже не досада, а только комментарий к этому новому функционалу): в полученном JSon свойства доменного класса находятся в другом порядке при использовании параметра проекции; и этот порядок не соответствует порядку интерфейса Projection. - person jplandrain; 28.04.2014
comment
Вероятно, это вызвано тем, что API отражения возвращает методы в разном порядке. Однако интерфейс проекции открыт для настройки с помощью аннотаций Джексона, поэтому @JsonPropertyOrder должен позволить вам настраивать вещи по мере необходимости. - person Oliver Drotbohm; 28.04.2014
comment
Подтверждаю: @JsonPropertyOrder работает нормально. Большое спасибо за вашу помощь. - person jplandrain; 02.05.2014
comment
Можно ли полностью отключить хал? Я хочу получить только JSON с идентификаторами объектов. - person Luis Vargas; 24.05.2014
comment
@bigluis Да, вы можете сделать это с помощью прогнозов: docs.spring.io/spring-data/rest/docs/current/reference/html/ Однако это может быть утомительной работой по определению проекций для всех ваших объектов. - person jplandrain; 29.05.2015
comment
вам не кажется это глупым? Если бы у меня было 100 моделей, мне нужно было бы создать 100 проекций. Я так не думаю. Я ищу флаг конфигурации, который позволяет мне отправлять Json или HalJson вместо HalJson - person Luis Vargas; 29.05.2015
comment
Привет, когда я использую заголовок: application/x-spring-data-verbose+json, список содержимого остается пустым. Я не понимаю, что я делаю неправильно. Более подробный вопрос здесь: stackoverflow.com/questions/29670835/ - person Yannic Klem; 18.06.2015
comment
Проблема, связанная с характером SDR только для гипермедиа, заключается в том, что простые оставшиеся API (то есть уровень 2 модели зрелости Ричардсона) являются основными и проверенными в производстве. Присяжные еще не определились с удобством использования API-интерфейсов Hypermedia (уровень 3). Привязка SDR к Hypermedia, по-видимому, обрекает его на экспериментальный статус, что затрудняет его продажу всем, кому нужны готовые к работе API — или я что-то упускаю? - person roger armstrong; 17.07.2015
comment
@rogerarmstrong Вам не хватает тысяч API, которые уже успешно используют эту модель даже в 2018 году. Ваши личные предпочтения затуманивают ваше суждение. - person Evert; 12.12.2018
comment
Для весенней загрузки 2.0.5 я просто получаю 406 Not Acceptable в качестве ответа, поэтому application/x-spring-data-verbose+json, похоже, постепенно прекращается. - person Christian; 03.11.2020

Итак, вы хотите 2 вещи:

1) избавиться от поля _links
2) включить связанное поле sector

Возможное решение (у меня работает :D)

1) избавиться от _links
Для этого создайте класс ниже:

[... package declaration, imports ...]
public class MyRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
    public MyRepositoryRestMvcConfiguration(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
        super(context, conversionService);
    }

    @Bean
    protected LinkCollector linkCollector() {
        return new LinkCollector(persistentEntities(), selfLinkProvider(), associationLinks()) {
            public Links getLinksFor(Object object, List<Link> existingLinks) {
                return new Links();
            }
        };
    }
}

и используйте его, например:

[... package declaration, imports ...]
@SpringBootApplication
@Import({MyRepositoryRestMvcConfiguration.class})
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Я почти уверен (99%, но не проверено), что вам не понадобится этот класс для удаления _links для связанных объектов/объектов, включенных, как показано в следующем пункте (2).

2) включить соответствующее поле sector
Для этого вы можете использовать Выдержки (специально сделанные для этого сценария). Поскольку пример Spring очень красноречив и глупо просто копировать его здесь, я просто укажу на него: https://docs.spring.io/spring-data/rest/docs/3.1.x/reference/html/#projections-excerpts.excerpting-commonly-accessed-data.
Но только для записи и для вашего удобства я вставлю основные части весеннего примера:

@Projection(name = "inlineAddress", types = { Person.class }) 
interface InlineAddress {
  String getFirstName();
  String getLastName();
  Address getAddress(); 
}

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

@RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}

чтобы получить это (при использовании MyRepositoryRestMvcConfiguration):

{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "address" : { 
    "street": "Bag End",
    "state": "The Shire",
    "country": "Middle Earth"
  }
}

Для вас sector эквивалентно address.

Заключительные примечания

При возврате массивов поле _links удаляться не будет (слишком навязчиво это делать); в итоге у вас будет что-то вроде этого:

{
    "_embedded" : {
        "persons" : [ {person1}, {person2}, ..., {personN} ]
    },
    "_links" : {
        e.g. first, next, last, self, profile
    },
    "page" : {
      "size" : 1,
      "totalElements" : 10,
      "totalPages" : 10,
      "number" : 0
    }
}

Как видите, даже если бы мы удалили _links, этого все равно было бы недостаточно; вероятно, также хотелось бы заменить _embedded на persons, что привело бы к менее удобному коду (слишком много пружинных навязчивых переопределений). Но если кто-то действительно хочет и их, он должен начать проверять RepositoryRestMvcConfiguration и RepositoryEntityController.getCollectionResource.

Spring развивается, поэтому я чувствую необходимость указать, что это работает, по крайней мере, с:

spring-data-rest-webmvc 3.1.3.RELEASE

или, если вы предпочитаете весеннюю загрузочную версию:

spring-boot-starter-parent 2.1.1.RELEASE
person adrhc    schedule 12.12.2018
comment
Мне нравится ваш ответ, он предлагает хороший учебник для тех, кто не знает об отрывках, он может помочь другим людям. Однако вопрос восходит к 2014 году, и я смог добиться того, что пытался сделать более элегантным способом с помощью Projections, согласно ответу Оливера Дротбома (он является руководителем проекта Spring Data). Я только что проверил, что я сделал в коде: чтобы включить связанное поле sector, моя основная проекция возвращает SectorProjection, а не объект Sector. Таким образом, я вообще не получаю ссылки. Ваш ответ действителен, если кто-то хочет сохранить и ссылки - person jplandrain; 14.12.2018
comment
Спасибо за высокую оценку :). 1-м пунктом я показал как убрать поле _links; это действительно не работает для массивов, но с решением Projections я почти уверен (99%), что то же самое (и для _embedded, и для страницы тоже). - person adrhc; 14.12.2018
comment
Да, в конце очень похоже. Но я могу выбрать только один хороший ответ, извините. И я уверен, что ваш ответ будет оценен сообществом. - person jplandrain; 14.12.2018