Jak poprawnie korzystać z PagedResourcesAssembler z danych Spring?
Używam Spring 4.0.0.Wydanie, Spring Data Commons 1.7.0.M1, Spring Hate 0.8.0.RELEASE
Mój zasób to proste POJO:
public class UserResource extends ResourceSupport { ... }
Mój asembler zasobów konwertuje Obiekty użytkownika do obiektów 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
}
}
Wewnątrz mojego kontrolera UserController Chcę pobrać Page<User>
z mojego serwisu, a następnie przekonwertować go do PagedResources<UserResource>
za pomocą PagedResourcesAssembler
, Jak pokazano tutaj: 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);
}
To nie wywołuje UserResourceAssembler
i po prostu Zawartość User
są zwracane zamiast mojego zwyczaju UserResource
.
Zwracanie pojedynczego zasobu działa:
@Autowired
UserResourceAssembler assembler;
@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
return assembler.toResource(service.getById(id));
}
The PagedResourcesAssembler
chce jakiegoś ogólnego argumentu, ale wtedy nie mogę użyć T toResource(T)
, ponieważ nie chcę konwertować mojego Page<User>
na PagedResources<User>
, zwłaszcza, że User
jest POJO i nie ma zasobów.
Więc pytanie brzmi: Jak to działa?
EDIT:
My 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);
}
/* ... */
}
Rozwiązanie:
@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 answers
Wydaje się, że już dowiedziałeś się o właściwym sposobie użycia, ale chciałbym przejść do niektórych szczegółów tutaj trochę dla innych, aby znaleźć, jak również. O PagedResourceAssembler
w tej odpowiedzi zająłem się podobnymi szczegółami.
Modele reprezentacji
Spring HATEOAS oferuje różnorodne klasy bazowe dla modeli reprezentacji, które ułatwiają tworzenie reprezentacji wyposażonych w linki. Istnieją trzy rodzaje klas dostarczane po wyjęciu z pudełka:
-
Resource
- zasób przedmiotów. Skutecznie owijać wokół jakiegoś DTO lub podmiotu, który przechwytuje pojedynczy element i wzbogaca go o linki. -
Resources
- zbiór zasobów, który może być zbiorem czegoś, ale zazwyczaj jest zbioremResource
instancji. -
PagedResources
- rozszerzenieResources
, które przechwytuje dodatkowe informacje o paginacji, takie jak liczba wszystkich stron itp.
Wszystkie te klasy wywodzą się z ResourceSupport
, który jest podstawowym kontenerem dla instancji Link
.
Resource assemblers
A ResourceAssembler
jest teraz komponentem łagodzącym do konwersji obiektów domeny lub DTOs na takie instancje zasobów. Ważne jest to, że zamienia jeden obiekt źródłowy w jeden obiekt docelowy.
Tak więc PagedResourcesAssembler
pobierze instancję Spring Data Page
i przekształci ją w instancję PagedResources
, oceniając Page
i tworząc niezbędne PageMetadata
, a także linki prev
i next
do nawigacji po stronach. Przez domyślna - i to jest prawdopodobnie interesująca część tutaj-będzie używać zwykłej SimplePagedResourceAssembler
(wewnętrznej klasy PRA
) do przekształcania poszczególnych elementów strony w zagnieżdżone instancje Resource
.
Aby umożliwić dostosowanie tego, PRA
ma dodatkowe metody toResource(…)
, które biorą delegata ResourceAssembler
do przetwarzania poszczególnych elementów. Więc kończysz z czymś takim:
class UserResource extends ResourceSupport { … }
class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }
A kod klienta wygląda teraz mniej więcej tak:
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
Od nadchodzące Spring Data Commons 1.7 RC1 (i Spring HATEOAS 0.9 Transitional) linki prev
i next
będą generowane jako szablony Uri zgodne z RFC6540 , aby ujawnić parametry żądania paginacji skonfigurowane w HandlerMethodArgumentResolvers
dla Pageable
i Sort
.
Konfiguracja, którą pokazałeś powyżej, może zostać uproszczona przez dodanie adnotacji do klasy config za pomocą @EnableSpringDataWebSupport
, co pozwoli Ci pozbyć się wszystkich jawnych deklaracji bean.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:51
ALTERNATYWNY SPOSÓB
Innym sposobem jest użycie nagłówka HTTP Range (więcej w RFC 7233). Możesz zdefiniować nagłówek HTTP w ten sposób:
Range: resources=20-41
Oznacza to, że chcesz uzyskać zasoby od 20 do 41 (włącznie). W ten sposób użytkownicy API otrzymują dokładnie zdefiniowane zasoby.
To po prostu alternatywny sposób. Range jest często używany z innymi jednostkami (jak bajty itp.)SPOSÓB ZALECANY
Jeśli chcesz pracować z paginacją i mieć naprawdę odpowiednie API (Hypermedia / HATEOAS włączone) następnie polecam dodać stronę i PageSize do adresu URL. Przykład:
http://host.loc/articles?Page=1&PageSize=20
Następnie możesz odczytać te dane w swoim Baseapicontrolle i utworzyć jakiś obiekt QueryFilter we wszystkich żądaniach:
{
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;
}
Twój api powinien zwrócić jakąś specjalną kolekcję z informacją o liczbie elementów.
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; }
}
Twoje klasy modelowe mogą dziedziczyć jakąś klasę z obsługą paginacji:
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; }
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-21 08:46:04