Как правильно использовать 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);
}
2 ответов:
Вы, кажется, уже узнали о правильном способе использования, но я хотел бы немного углубиться в некоторые детали здесь, чтобы другие также могли найти. Я подробно остановился на
PagedResourceAssemblerв этом ответе.Модели представления
Spring HATEOAS поставляется с различными базовыми классами для моделей представления, которые позволяют легко создавать представления, снабженные ссылками. Существует три типа классов, предоставляемых из коробки:Все эти классы являются производными от
Resource- элемент ресурса. Эффективно обернуть вокруг некоторого DTO или сущности, которая захватывает один элемент и обогащает его ссылками.Resources- ресурс коллекции, который может быть коллекцией чего-либо, но обычно представляет собой коллекцию экземпляровResource.PagedResources- РасширениеResources, которое захватывает дополнительную информацию о пагинации, такую как общее количество страниц и т. д.ResourceSupport, который является базовым контейнером для экземпляровLink.Ассемблеры ресурсов
A
Таким образом,ResourceAssemblerтеперь является смягчающим компонентом для преобразования объектов домена или DTOs в такие экземпляры ресурсов. Здесь важно то, что он превращает один исходный объект водин целевой объект.PagedResourcesAssemblerвозьмет экземпляр Spring DataPageи преобразует его в экземпляр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 можно определить следующим образом:
Это означает, что вы хотите получить ресурс от 20 до 41 (включая). Этот способ позволяет консумантам API получать точно определенные ресурсы.Range: resources=20-41Это просто альтернативный путь. Диапазон часто используется с другими единицами измерения (например, байты и т. д.)
РЕКОМЕНДУЕМЫЙ СПОСОБ
Если вы хотите работать с пагинацией и иметь действительно применимый 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