Ответ Spring REST отличается в пользовательском контроллере

У меня есть несколько контроллеров, которые автоматически создают REST конечные точки.

@RepositoryRestResource(collectionResourceRel = "books", path = "books")
public interface BooksRepository extends CrudRepository<Books, Integer> {
    public Page<Books> findTopByNameOrderByFilenameDesc(String name);
}

Когда я посещаю: http://localhost:8080/Books

Я вернусь:

{
    "_embedded": {
        "Books": [{
            "id": ,
            "filename": "Test123",
            "name": "test123",
            "_links": {
                "self": {
                    "href": "http://localhost:8080/books/123"
                },
                "Books": {
                    "href": "http://localhost:8080/books/123"
                }
            }
        }]
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/books"
        },
        "profile": {
            "href": "http://localhost:8080/profile/books"
        },
        "search": {
            "href": "http://localhost:8080/books/search"
        },
        "page": {
            "size": 20,
            "totalElements": 81,
            "totalPages": 5,
            "number": 0
        }
    }
}

Когда я создаю свой собственный контроллер:

@Controller
@RequestMapping(value = "/CustomBooks")
public class CustomBooksController {
    @Autowired
    public CustomBookService customBookService;

    @RequestMapping("/search")
    @ResponseBody
    public Page<Book> search(@RequestParam(value = "q", required = false) String query,
                                 @PageableDefault(page = 0, size = 20) Pageable pageable) {
        return customBookService.findAll();
    }
}

Я получу ответ, который совсем не похож на автоматически сгенерированный ответ контроллера:

{
    "content": [{
        "filename": "Test123",
        "name" : "test123"
    }],
    "totalPages": 5,
    "totalElements": 81,
    "size": 20,
    "number": 0,
}

Что мне нужно сделать, чтобы мой ответ выглядел как автоматически сгенерированный ответ? Я хочу, чтобы он был последовательным, поэтому мне не нужно переписывать код для другого ответа. Должен ли я делать это по-другому?


Редактировать: Нашел это: Включить сериализацию HAL в Spring Boot для пользовательского метода контроллера

Но я не понимаю, что мне нужно изменить в моем REST-контроллере, чтобы включить: PersistentEntityResourceAssembler. Я искал в Google PersistentEntityResourceAssembler, но он продолжает возвращать меня на похожие страницы без особого примера (или пример не работает для меня).


person Kevin Vasko    schedule 01.12.2016    source источник
comment
Вы пробовали @ResourceRestController?   -  person chrylis -cautiouslyoptimistic-    schedule 02.12.2016
comment
Не уверен, правильно ли я понял ваш вопрос, но я думаю, что вы ищете HATEOAS spring. io/guides/gs/rest-hateoas   -  person Markus    schedule 02.12.2016
comment
Один возвращает страницу‹Книга›, другой — страницу‹Книги› (обратите внимание на s), но я предполагаю, что это опечатка?   -  person Jens Schauder    schedule 02.12.2016
comment
@chrylis Я не думаю, что есть @ResourceRestController? Вы имеете в виду @RestController?   -  person Kevin Vasko    schedule 02.12.2016
comment
@KevinVasko Моя ошибка - это @RepositoryRestController, как заметил Алекс.   -  person chrylis -cautiouslyoptimistic-    schedule 03.12.2016
comment
@Chrylis Спасибо! Я нашел его около 15 минут назад в поисках аннотаций об отдыхе и контроллере, догадался, и на этом все. Но по какой-то причине эта аннотация заставляет мою аннотацию @PageableDefault перестать работать. Знаете ли вы, почему это может привести к тому, что он перестанет функционировать? Если у меня есть @Controller, аннотация работает, при использовании @RepositoryRestController мое значение для страницы всегда равно нулю.   -  person Kevin Vasko    schedule 03.12.2016
comment
Я не знаю. Я считаю Spring Data REST интересной игрушкой, но слишком нестабильной для серьезного производственного использования. У него есть ряд непреодолимых конструктивных недостатков, которые я был поражен, увидев от главного дизайнера.   -  person chrylis -cautiouslyoptimistic-    schedule 03.12.2016
comment
@chrylis, чтобы не вдаваться в дизайн приложений в разделе комментариев, но что бы вы порекомендовали использовать для создания моего REST API?   -  person Kevin Vasko    schedule 03.12.2016
comment
Это ушло бы далеко в сорняки. Короче говоря, у меня серьезный скептицизм по поводу провозглашенных достоинств HATEOAS.   -  person chrylis -cautiouslyoptimistic-    schedule 03.12.2016


Ответы (1)


Как предложил @chrylis, вы должны заменить аннотацию @Controller на @RepositoryRestController для spring-data-rest, чтобы вызвать его ResourceProcessors для настройки данного ресурса.

Чтобы ваш ресурс соответствовал спецификации HATEOAS (например, ваш BooksRepository spring-data-rest), ваш тип возвращаемого объявления метода должен быть похож на HttpEntity<PagedResources<Resource<Books>>> Для преобразования вашего объекта Page в PagedResources:

  • Вам нужно автосвязать этот объект.

    @Autowired private PagedResourcesAssembler<Books> bookAssembler;

  • Ваше заявление о возврате должно быть похоже на

    return new ResponseEntity<>(bookAssembler.toResource(customBookService.findAll()), HttpStatus.OK);

Эти изменения должны помочь вам получить ответ, совместимый с org.springframework.hateoas.Resources, содержащий атрибуты "_embedded" и "_links".

person Alex Ciocan    schedule 02.12.2016
comment
Для справки вы можете использовать этот ответ [ссылка] stackoverflow.com/questions/31758862/ - person Alex Ciocan; 02.12.2016
comment
Вы имеете в виду @RestController? Я не вижу @ResourceRestController нигде в сети. Когда я добавляю @Autowired private PagedResourcesAssembler‹Books›, intelliJ жалуется, что не может его автоматически связать. Он компилируется, и Springboot запускается. Однако, когда я звоню своему контроллеру, я получаю эту ошибку. Произошла непредвиденная ошибка (тип = внутренняя ошибка сервера, статус = 500). Не удалось маршалировать [PagedResource { content: [Resource { content:.... кроме того, эта страница с ошибкой находится в XML, а не в JSON. Неважно, что я беру это из Solr, не так ли? Книги — моя модель. - person Kevin Vasko; 02.12.2016
comment
Чтобы иметь вышеуказанную функциональность, ваш CustomBooksController должен находиться в том же проекте, что и ваш класс BooksRepository. - person Alex Ciocan; 02.12.2016
comment
com.myproject.repositories содержит мой BooksRepository, а CustomBooksController находится в пакете com.myproject.controllers. - person Kevin Vasko; 02.12.2016
comment
Исправил опечатку в аннотации @ResourceRestController должно быть @RepositoryRestController. Дайте мне знать, если это решит вашу проблему. - person Alex Ciocan; 02.12.2016
comment
Хорошо, это решение работает (частично). Похоже, это был @RepositoryRestController. По какой-то странной причине pageable всегда возвращается к нулю, несмотря ни на что сейчас. @PageableDefault (page = 0, size = 20) Pageable pageable в моем контроллере. Когда он передает это моему findBook(query, pageable), он выдает ошибку, говоря, что pageable имеет значение null (почти как если бы @PageableDefault работает). Похоже, это произошло после того, как я изменил его с @Controller на @RepoistoryRestController. - person Kevin Vasko; 03.12.2016
comment
Пара вопросов - внедрение конструктора (обычно), а затем я полагаю, что SDR автоматически обработает сборку ресурсов, если вы просто вернете Collection<Book>. Использование ResponseEntity необходимо только в том случае, если вам нужно программно установить заголовки или статус ответа (вместо использования аннотаций или исключений). Это должно значительно упростить код. - person chrylis -cautiouslyoptimistic-; 03.12.2016
comment
Если вы хотите вернуть один объект, используйте PersistentEntityResourceAssembler. Он будет автоматически внедрен, если присутствует в объявлении метода (@AlexCiocan, приятно видеть ваши ответы здесь :). - person Adam Bogdan Boczek; 18.02.2017