Как вернуть ответы в стиле @RepositoryRestResource от @RestController

Использование @RepositoryRestResource генерирует пути и внедряет все необходимые ссылки HATEOAS для REST API, но когда я возвращаю те же результаты из репозитория с помощью контроллера, структура JSON отличается и ссылок HATEOAS нет.

Как мне вернуть ту же структуру JSON от контроллера, что и пути, сгенерированные RepositoryRestResource?

// /accounts (RepositoryRestResource JSON structure)
{
    _embedded: {
        accounts: []
    },
    _links: {
        first: {},
        self: {},
        next: {},
        last: {},
        profile: {},
        search: {}
    },
    page: {
    size: 20,
    totalElements: 35,
    totalPages: 2,
    number: 0
    }
}

// /my-accounts (RestController JSON structure)
{
    content: [ ... ], // accounts
    pageable: {},
    totalPages: 1,
    totalElements: 2,
    last: true,
    size: 20,
    number: 0,
    sort: {},
    numberOfElements: 2,
    first: true
}

Репозиторий REST:

@RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts", itemResourceRel = "account")
public interface AccountRepository extends PagingAndSortingRepository<Account, Long> {

    @RestResource(path = "owner", rel = "owner")
    Page<Account> findByOwner(@Param("username") String owner,Pageable p);
}

Контроллер REST:

@RestController
public class AccountController {

    private AccountRepository repo;

    @Autowired
    public AccountController(AccountRepository repo) {
        this.repo = repo;
    }

    @RequestMapping(
        path = "/my-accounts",
        method = RequestMethod.GET,
        produces = "application/hal+json")
    public ResponseEntity<Page<Account>> getStatus(
        @RequestParam(value = "page", defaultValue = "0", required = false) int page,
        @RequestParam(value = "size", defaultValue = "20", required = false) int size,
        Authentication authentication) {

        String username =  authentication.getName();

        Page<Account> accounts = repo.findByOwner(username, PageRequest.of(page, size));

        return ResponseEntity.ok(accounts);
    }
}

person Your Friend Ken    schedule 05.07.2018    source источник


Ответы (2)


По сути, Spring Data REST - это просто реализация по умолчанию стандартного кода (например, контроллеров), который люди обычно пишут, выставляя репозитории Spring Data через REST и используя Spring HATEOAS, т.е. пытаясь воспроизвести точно такой же эффект с вашим рукописным контроллером, означает просто писать весь Spring Data REST самостоятельно, так что это плохая идея. К счастью, некоторые части легко воспроизвести.

Если вы говорите только о добавлении ссылок на страницы к выходным данным вашего контроллера (и не реализации других вещей, таких как контроллер поиска, ссылка на который присутствует в вашем образце выходных данных контроллера Spring Data REST), вы можете взглянуть на как это делает Spring Data REST. Он использует Spring Data PagedResourcesAssembler, который принимает Page и создает ресурс HATEOAS с необходимыми навигационными ссылками.

Итак, чтобы добавить ссылки на страницы, вы должны внедрить экземпляр PagedResourcesAssembler в свой контроллер и использовать его:

public ResponseEntity<PagedResources> getStatus(
    @RequestParam(value = "page", defaultValue = "0", required = false) int page,
    @RequestParam(value = "size", defaultValue = "20", required = false) int size,
    Authentication authentication,
    PagedResourcesAssembler assembler) {

    String username =  authentication.getName();

    Page<Account> accounts = repo.findByOwner(username, PageRequest.of(page, size));

    return ResponseEntity.ok(assembler.toResource(accounts));
}
person Danila Kiver    schedule 05.07.2018
comment
Это работает! Я знал, что должен быть способ добраться до ассемблера, используемого RepositoryRestResource. Спасибо! - person Your Friend Ken; 05.07.2018

Не уверен, но это может помочь:

return ResponseEntity.ok(new org.springframework.hateoas.Resource<>(accounts));

В противном случае вы можете обернуть учетные записи в класс, расширяющий ResourceSupport. Так что просто создайте какой-нибудь класс AccountSupport extends ResourceSupport и добавьте туда необходимые ссылки. Он имеет множество служебных методов, таких как

add(linkTo(AccountController.class).withSelfRel());

или для ссылок на отдельные Учетные записи:

add(linkTo(AccountController.class).slash(idOfYourAccountInstance).withSelfRel())
person Sebastiaan van den Broek    schedule 05.07.2018
comment
Нет, похоже, это не имеет значения ????, это тот же самый JSON, который контроллер создавал раньше. - person Your Friend Ken; 05.07.2018
comment
Извините, у меня был этот код в качестве альтернативы в каком-то старом репозитории, но я только что проверил его, и вы правы. В этом репо фактическое использованное решение было тем, что я сейчас отредактировал в ответ. - person Sebastiaan van den Broek; 05.07.2018