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



Я использую Spring 4.0.0.Релиз, Весна Data Commons 1.7.0.M1, Весна 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 возвращается вместо моего пользовательского 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> в PagedResources<User>, особенно потому, что User - это POJO и никакой ресурс.



Таким образом, возникает вопрос: как это работает?

Правка:



Мой 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);
}
783   2  

2 ответов:

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

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

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

Ассемблеры ресурсов

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

Таким образом, PagedResourcesAssemblerвозьмет экземпляр Spring Data 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);

Перспективы

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

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

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

Другим способом является использование заголовка Range HTTP (Подробнее читайте в RFC 7233). Заголовок HTTP можно определить следующим образом:

Range: resources=20-41
Это означает, что вы хотите получить ресурс от 20 до 41 (включая). Этот способ позволяет консумантам API получать точно определенные ресурсы.

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

РЕКОМЕНДУЕМЫЙ СПОСОБ

Если вы хотите работать с пагинацией и иметь действительно применимый API (включая hypermedia / 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; }
}

Comments

    Ничего не найдено.