Как правильно использовать PagedResourcesAssembler из данных Spring?

я использую Spring 4.0.0.Выпуск, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.Отпустите

мой ресурс-это простой POJO:

public class UserResource extends ResourceSupport { ... }

мой ассемблер ресурсов преобразует пользовательские объекты в объекты UserResource:

@Component
public class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { 
    public UserResourceAssembler() {
        super(UserController.class, UserResource.class);
    }

    @Override
    public UserResource toResource(User entity) {
        // map User to UserResource
    }
}

внутри моего UserController я хочу получить Page<User> из моего сервиса, а затем преобразовать его в PagedResources<UserResource> используя PagedResourcesAssembler, как показано здесь: https://stackoverflow.com/a/16794740/1321564

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler assembler) {
    Page<User> u = service.get(p)
    return assembler.toResource(u);
}

это не вызов UserResourceAssembler и просто содержание User возвращаются вместо моего custom UserResource.

возврат одного ресурса работает:

@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
    return assembler.toResource(service.getById(id));
}

на PagedResourcesAssembler хочет какой-то универсальный аргумент, но тогда я не могу использовать T toResource(T), потому что я не хочу конвертировать мой Page<User> to PagedResources<User>, особенно User является POJO и нет ресурсов.

так вопрос в том, как это работает?

EDIT:

Мой WebMvcConfigurationSupport:

@Configuration
@ComponentScan
@EnableHypermediaSupport
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(pageableResolver());
        argumentResolvers.add(sortResolver());
        argumentResolvers.add(pagedResourcesAssemblerArgumentResolver());
    }

    @Bean
    public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
        return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
    }

    @Bean
    public HateoasSortHandlerMethodArgumentResolver sortResolver() {
        return new HateoasSortHandlerMethodArgumentResolver();
    }

    @Bean
    public PagedResourcesAssembler<?> pagedResourcesAssembler() {
        return new PagedResourcesAssembler<Object>(pageableResolver(), null);
    }

    @Bean
    public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
        return new PagedResourcesAssemblerArgumentResolver(pageableResolver(), null);
    }

    /* ... */
}

устранение:

@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler pagedAssembler) {
    Page<User> u = service.get(p)
    return pagedAssembler.toResource(u, assembler);
}

2 ответов


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

модели представления

Spring HATEOAS поставляется с различными базовыми классами для моделей представления, которые позволяют легко создавать представления, оснащенные ссылками. Существует три типа классов из box:

  • Resource - элемент ресурсов. Эффективно обернуть вокруг некоторого DTO или сущности, которая захватывает один пункт и обогащает его ссылками.
  • Resources - ресурс коллекции, который может быть коллекцией чего-то, но обычно является коллекцией Resource экземпляров.
  • PagedResources - расширение Resources это захватывает дополнительную информацию о разбиении на страницы, такую как количество общих страниц и т. д.

все из этих классов происходят от ResourceSupport, который является основным контейнером для Link экземпляров.

ресурс монтажники

A ResourceAssembler теперь является смягчающим компонентом для преобразования объектов домена или DTOs в такие экземпляры ресурсов. Важная часть здесь в том, что получается один источник один целевого объекта.

так PagedResourcesAssembler возьму весенние данные Page экземпляр и преобразовать его в PagedResources экземпляр путем оценки Page и создание необходимых PageMetadata а также prev и next ссылки для навигации по страницам. По умолчанию - и это, вероятно, интересная часть здесь - он будет использовать простой SimplePagedResourceAssembler (внутренний класс PRA) для преобразования отдельных элементов страницы во вложенных Resource экземпляров.

чтобы настроить это, PRA дополнительные toResource(…) методы, которые принимают делегат ResourceAssembler для обработки отдельных элементов. Так что вы в конечном итоге с чем-то вроде этого:

 class UserResource extends ResourceSupport { … }

 class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }

и код клиента теперь выглядит примерно так:

 PagedResourcesAssembler<User> parAssembler = … // obtain via DI
 UserResourceAssembler userResourceAssembler = … // obtain via DI

 Page<User> users = userRepository.findAll(new PageRequest(0, 10));

 // Tell PAR to use the user assembler for individual items.
 PagedResources<UserResource> pagedUserResource = parAssembler.toResource(
   users, userResourceAssembler);

Outlook

по состоянию на предстоящую весну Data Commons 1.7 RC1 (и Spring HATEOAS 0.9 транзитивно)prev и next ссылки будут создаваться как RFC6540 совместимые шаблоны URI для предоставления параметров запроса разбиения на страницы, настроенных в HandlerMethodArgumentResolvers for Pageable и Sort.

в конфигурация, показанная выше, может быть упрощена путем аннотирования класса config с помощью @EnableSpringDataWebSupport что позволит вам избавиться от всех явных объявлений bean.


АЛЬТЕРНАТИВНЫЙ СПОСОБ

другой способ-использовать HTTP-заголовок диапазона (подробнее в RFC 7233). Вы можете определить заголовок HTTP следующим образом:

Range: resources=20-41

это означает, что вы хотите получить ресурс от 20 до 41 (включая). Этот способ позволяет consuments API получать точно определенные ресурсы.

Это просто альтернативный способ. Диапазон часто используется с другими единицами (например, байтами и т. д.)

рекомендуется Путь

если вы хотите работать с разбиением на страницы и иметь действительно применимый API (гипермедиа / HATEOAS включены), то я рекомендую добавить страницу и PageSize к вашему URL. Пример:

http://host.loc/articles?Page=1&PageSize=20

затем вы можете прочитать эти данные в своем BaseApiController и создать некоторый объект QueryFilter во всех ваших запросах:

{
    var requestHelper = new RequestHelper(Request);

    int page = requestHelper.GetValueFromQueryString<int>("page");
    int pageSize = requestHelper.GetValueFromQueryString<int>("pagesize");

    var filter = new QueryFilter
    {
        Page = page != 0 ? page : DefaultPageNumber,
        PageSize = pageSize != 0 ? pageSize : DefaultPageSize
    };

    return filter;
}

ваш api должен возвращать некоторую специальную коллекцию с информацией о количестве элементов.

public class ApiCollection<T>
{
    public ApiCollection()
    {
        Data = new List<T>();
    }

    public ApiCollection(int? totalItems, int? totalPages)
    {
        Data = new List<T>();
        TotalItems = totalItems;
        TotalPages = totalPages;
    }

    public IEnumerable<T> Data { get; set; }

    public int? TotalItems { get; set; }
    public int? TotalPages { get; set; }
}

ваши модельные классы могут наследовать некоторый класс с поддержкой разбиения на страницы:

public abstract class ApiEntity
{
    public List<ApiLink> Links { get; set; }
}

public class ApiLink
{
    public ApiLink(string rel, string href)
    {
        Rel = rel;
        Href = href;
    }

    public string Href { get; set; }

    public string Rel { get; set; }
}