Могу ли я сделать настраиваемый контроллер зеркальным отображением форматирования классов, созданных Spring-Data-Rest / Spring-Hateoas?

Я пытаюсь сделать что-то, что, по моему мнению, должно быть очень простым. У меня есть объект Question, настройка с помощью spring -boot, spring-data-rest и spring-hateoas. Все основы работают нормально. Я хотел бы добавить настраиваемый контроллер, который возвращает List<Question> в том же формате, что и GET на мой URL-адрес Repository /questions, чтобы ответы между ними были совместимы.

Вот мой контроллер:

@Controller
public class QuestionListController {

    @Autowired private QuestionRepository questionRepository;

    @Autowired private PagedResourcesAssembler<Question> pagedResourcesAssembler;

    @Autowired private QuestionResourceAssembler questionResourceAssembler;

    @RequestMapping(
            value = "/api/questions/filter", method = RequestMethod.GET,
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody PagedResources<QuestionResource> filter(
            @RequestParam(value = "filter", required = false) String filter,
            Pageable p) {

        // Using queryDSL here to get a paged list of Questions
        Page<Question> page = 
            questionRepository.findAll(
                QuestionPredicate.findWithFilter(filter), p);

        // Option 1 - default resource assembler
        return pagedResourcesAssembler.toResource(page);

        // Option 2 - custom resource assembler
        return pagedResourcesAssembler.toResource(page, questionResourceAssembler);
    }

}

Вариант 1. Положитесь на предоставленный SimplePagedResourceAssembler

Проблема с этой опцией в том, что ни один из необходимых _links не отображается. Если бы это было исправлено, это было бы самым простым решением.

Вариант 2. Реализация моего ассемблера открытых ресурсов

Проблема с этим вариантом заключается в том, что реализация QuestionResourceAssembler в соответствии с документацией Spring-Hateoas приводит к путь, где QuestionResource оказывается почти дубликатом Question, а затем ассемблер должен вручную скопировать данные между двумя объектами, а мне нужно вручную собрать все соответствующие _links. Это кажется потраченным впустую.

Что делать?

Я знаю, что Spring уже сгенерировал код для всего этого при экспорте QuestionRepository. Могу ли я подключиться к этому коду и использовать его, чтобы гарантировать, что вывод моего контроллера будет бесшовным и взаимозаменяемым с сгенерированными ответами?


person JBCP    schedule 23.10.2014    source источник


Ответы (3)


Я нашел способ полностью имитировать поведение Spring Data Rest. Уловка заключается в использовании комбинации PagedResourcesAssembler и экземпляра PersistentEntityResourceAssembler с введенным аргументом. Просто определите свой контроллер следующим образом ...

@RepositoryRestController
@RequestMapping("...")
public class ThingController {

    @Autowired
    private PagedResourcesAssembler pagedResourcesAssembler;

    @SuppressWarnings("unchecked") // optional - ignores warning on return statement below...
    @RequestMapping(value = "...", method = RequestMethod.GET)
    @ResponseBody
    public PagedResources<PersistentEntityResource> customMethod(
            ...,
            Pageable pageable,
            // this gets automatically injected by Spring...
            PersistentEntityResourceAssembler resourceAssembler) {

        Page<MyEntity> page = ...;
        ...
        return pagedResourcesAssembler.toResource(page, resourceAssembler);
    }
}

Это работает благодаря наличию PersistentEntityResourceAssemblerArgumentResolver, который Spring использует для внедрения PersistentEntityResourceAssembler за вас. Результат - именно то, что вы ожидаете от одного из методов запроса вашего репозитория!

person Ryan    schedule 28.04.2015
comment
Хорошо, я попробую. - person JBCP; 29.04.2015
comment
См. Также stackoverflow.com/questions/31758862/. - person ; 11.10.2015
comment
Небольшое дополнение: вы можете предотвратить непроверенное предупреждение, когда ваш метод напрямую возвращает PagedResource<MyEntity> это то, что возвращает (paged) resourceAssembler.toResource - person Robert; 06.01.2017
comment
Это сработало, за исключением случаев, когда результатов не было. В этом случае в ответе REST не было ключа _embedded, в отличие от автоматически сгенерированного контроллера REST / HATEOAS. В этом случае мне пришлось вручную вызвать pagedResourcesAssembler.toEmptyResource. - person alalonde; 27.02.2017
comment
@alalonde, возможно, поведение Spring Data REST изменилось, но я также не получаю _embedded, если коллекция пуста в сгенерированных репозиториях HATEOAS. - person Hubert Grzeskowiak; 08.06.2017
comment
@Robert Извините, но я не могу удалить не отмеченное предупреждение в моем коде. Я пробовал то, что вы предлагали, но всегда предупреждал о методе toResource (). Спасибо! - person drenda; 14.11.2017
comment
SDR не может внедрить PersistentEntityResourceAssembler с помощью Spring Boot 2.1.0. Это дает java.lang.IllegalArgumentException: entities is marked @NonNull but is null. - person Jefferson Lima; 03.01.2019

Обновленный ответ на этот старый вопрос: теперь вы можете сделать это с помощью PersistentEntityResourceAssembler

Внутри вашего @RepositoryRestController:

@RequestMapping(value = "somePath", method = POST)
public @ResponseBody PersistentEntityResource postEntity(@RequestBody Resource<EntityModel> newEntityResource, PersistentEntityResourceAssembler resourceAssembler)
{
  EntityModel newEntity = newEntityResource.getContent();
  // ... do something additional with new Entity if you want here ...  
  EntityModel savedEntity = entityRepo.save(newEntity);

  return resourceAssembler.toResource(savedEntity);  // this will create the complete HATEOAS response
}
person Robert    schedule 06.01.2017
comment
Можно ли принять ссылку на существующий ресурс в аргументах метода контроллера и автоматически преобразовать ее в сущность? - person aycanadal; 15.02.2017
comment
Да, если ваша сущность связана с другой дочерней сущностью, вы можете просто отправить URI для создания этой ссылки. например HTTP POST / path / to / parent / entity Полезная нагрузка предназначена для примера {someAttr: "example Value", linkToChildEntity: "/path/to/child/entity/<id>" } Надеюсь, это поможет. См. этот вопрос о переполнении стека для гораздо более подробной информации - person Robert; 17.02.2017
comment
Я имею в виду кастомный контроллер. Я знаю, что остальные данные Spring делают это. Вопрос в том, как я могу написать контроллер, который может делать то же самое? - person aycanadal; 18.02.2017
comment
как работать с нулевыми значениями? ... PersistentEntity не может быть нулевым! - person Rafael; 30.03.2017
comment
Также я не уверен, как обращаться с коллекциями, например ПОЛУЧИТЬ / найти все операции. Не могли бы вы привести какой-нибудь интересный пример? - person Rafael; 30.03.2017
comment
@Rafael IMHO WebService HATEOAS REST никогда не должен возвращать просто null. Он всегда должен возвращать как минимум пустой массив JSON: {} - person Robert; 31.03.2017
comment
Мой рабочий пример отмечен здесь: github.com/Doogiemuc/liquido-backend-spring/blob/master/src/ - person Robert; 31.03.2017
comment
@Robert, я полностью согласен ... я получаю эту ошибку, даже если коллекция не пуста и не пуста ... может быть, у меня что-то не так. Спасибо. - person Rafael; 31.03.2017
comment
См. jira.spring.io/browse/DATAREST-657 перед использованием этого подхода. - person Miguel Pereira; 16.10.2017
comment
@aycanadal относительно вашего вопроса, если вы берете свою сущность в качестве параметра, используя Ресурс ‹› для инкапсуляции сущности, SDR должен переводить ссылки ассоциации на их фактические сущности. В приведенном выше примере: @RequestBody Resource<EntityModel> - person Davi Cavalcanti; 23.10.2018

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

Прочитав реализацию SimplePagedResourceAssembler, я понял, что гибридное решение может работать. Предоставленный класс Resource<?> правильно отображает объекты, но не включает ссылки, поэтому все, что вам нужно сделать, это добавить их.

Моя QuestionResourceAssembler реализация выглядит так:

@Component
public class QuestionResourceAssembler implements ResourceAssembler<Question, Resource<Question>> {

    @Autowired EntityLinks entityLinks;

    @Override
    public Resource<Question> toResource(Question question) {
        Resource<Question> resource = new Resource<Question>(question);

        final LinkBuilder lb = 
            entityLinks.linkForSingleResource(Question.class, question.getId());

        resource.add(lb.withSelfRel());
        resource.add(lb.slash("answers").withRel("answers"));
        // other links

        return resource;
    }
}

Как только это будет сделано, я использовал в своем контроллере вариант 2, описанный выше:

    return pagedResourcesAssembler.toResource(page, questionResourceAssembler);

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

person JBCP    schedule 24.10.2014
comment
Конечно, похоже, что Spring могла бы сделать это намного проще. Разве тот факт, что вам нужно добавлять свои собственные ссылки, по-прежнему не приводит к потенциально несвязному API (как вы указывали в другом месте)? И вам нужно везде использовать Pages? Когда я просто пытаюсь вернуть список ‹Resource ‹MyObj››, я получаю рекурсию из-за двунаправленных сопоставлений гибернации. Поэтому мне нужно либо добавить @ JsonIgnore, либо вместо этого использовать PagedResourcesAssembler. - person gyoder; 02.01.2015
comment
Я думаю, что вы хотите использовать PagedResources и Pageable каждый раз, когда возвращаете более одного объекта, просто для безопасности. Определенно, это могло бы быть намного проще, но на самом деле вы не получите несвязный API, если будете полагаться на собственный конструктор ссылок Spring. Возможно, вы не получите ссылки на полезные места назначения, но поскольку все, что имеет значение, остается за Контроллером, это имеет определенный смысл. Его настоящий Spring, вероятно, может предоставить полезные ссылки на основе возвращаемого типа объекта. - person JBCP; 02.01.2015
comment
В 2016 году это все еще лучший / единственный способ получить поведение spring-data-rest esque при использовании контроллера / настраиваемого репозитория? Это довольно неприятно. - person Casey; 30.10.2016
comment
Извините, я больше не работаю со Spring, поэтому не могу ответить, что такое новейшая техника. - person JBCP; 30.10.2016