Подтвердить что ты не робот

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

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

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

@Controller
public class QuestionListController {

    @Autowired private QuestionRepository questionRepository;

    @Autowired private PagedResourcesAssembler<Question> pagedResourcesAssembler;

    @Autowired private QuestionResourceAssembler questionResourceAssembler;

    @RequestMapping(
            value = "/api/info/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 documentation ведет по пути, где QuestionResource заканчивается как почти дубликат Question, а затем ассемблеру необходимо вручную скопировать данные между двумя объектами, и мне нужно собрать все соответствующие _links вручную. Похоже, это сильно расходуется.

Что делать?

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

4b9b3361

Ответ 1

Я нашел способ полностью имитировать поведение 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 для вас. Результат - это то, что вы ожидаете от одного из методов запроса репозитория!

Ответ 2

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

После прочтения реализации 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);

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

Ответ 3

Обновленный ответ на этот старый вопрос: теперь вы можете сделать это с помощью 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
}