Да се ​​деактивира ли Hypertext Application Language (HAL) в JSON?

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

Как мога да деактивирам Hypertext Application Language (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, сървърът ще върне специфичния за Spring Data JSON формат, освен ако изрично не поискате application/hal+json. Вярно е, че опцията за конфигурация вероятно е малко подвеждаща, така че подадох DATAREST-294, за да подобря това . Проблемът беше решен в 2.1 RC1 (Dijkstra) 2014.

Обърнете внимание, че действително се нуждаем от хипермедиен формат, за да можем да изразим връзките между управляваните ресурси и да позволим откриваемостта на сървъра. Така че няма начин да се отървете напълно от него. Това се дължи най-вече на факта, че лесно бихте могли да сринете сървъра, ако разкриете обекти, които имат двупосочни връзки или съставят огромна обектна графика.

Вграждане на свързани обекти

Ако никога не искате да имате свързани сектори и винаги да ги вграждате, една от опциите е просто да изключите SectorRepository от експортиране като REST ресурс на първо място. Можете да постигнете това, като анотирате интерфейса на хранилището с @RepositoryRestResource(exported = false).

За да получите върнато представяне, както сте публикували в долния си пример, погледнете функцията проекции въведен в 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 за отражение, който връща методите в различен ред. Интерфейсът на проекцията обаче е отворен за персонализиране чрез пояснения на Jackson, така че @JsonPropertyOrder трябва да ви позволи да персонализирате нещата според нуждите. - person Oliver Drotbohm; 28.04.2014
comment
Потвърждавам: @JsonPropertyOrder работи добре. Благодаря ви много за цялата ви помощ. - person jplandrain; 02.05.2014
comment
Възможно ли е да деактивирате напълно hal? Искам да извлека само JSON с идентификатори на обекти. - person Luis Vargas; 24.05.2014
comment
@bigluis Да, можете да го направите с помощта на Projections: 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 на модела на зрялост на Ричардсън) са основни и доказани в производството. Журито все още не е оценило използваемостта на Hypermedia APIs (ниво 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-data-rest-webmvc 3.1.3.RELEASE

или, ако предпочитате пролетна версия за зареждане:

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