Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
REST Pagination in Spring with Link header

RESTful pagination in Spring using Link header

Pagination is a mechanism for managing big result sets in any kind of application. This quick tutorial focuses on implementing pagination in a RESTful API, using Spring MVC emoji-leaves and Spring Data without the help of the Spring HATEOAS project.

Continue reading ...

package com.matchilling.api.rest.data
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import org.springframework.boot.jackson.JsonComponent
import org.springframework.data.domain.PageImpl
import java.io.IOException
@JsonComponent
class PageSerializer : JsonSerializer<PageImpl<*>>() {
@Throws(IOException::class)
override fun serialize(
page: PageImpl<*>,
jsonGenerator: JsonGenerator,
serializerProvider: SerializerProvider
) {
jsonGenerator.writeObject(page.content)
}
}
package com.matchilling.api.rest.servlet
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.MethodParameter
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
import org.springframework.web.util.UriComponentsBuilder
@RestControllerAdvice
class PaginatedResponseAdvice<T>(
@Value("\${spring.data.web.pageable.one-indexed-parameters}")
private val oneIndexed: Boolean
) : ResponseBodyAdvice<T> {
override fun supports(
returnType: MethodParameter,
converterType: Class<out HttpMessageConverter<*>>
): Boolean {
return PageImpl::class.java.isAssignableFrom(returnType.parameterType)
}
override fun beforeBodyWrite(
page: T?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): T? {
if (page !is PageImpl<*>) {
return page
}
val headers = response.headers
headers.set(
"Access-Control-Expose-Headers",
"Link,Page-Number,Page-Size,Total-Elements,Total-Pages"
)
val links = page.links(request)
if (links.isNotBlank()) {
headers.set("Link", links)
}
val pageNumber = if (oneIndexed)
page.number.plus(1)
else
page.number
headers.set("Page-Number", pageNumber.toString())
headers.set("Page-Size", page.size.toString())
headers.set("Total-Elements", page.totalElements.toString())
headers.set("Total-Pages", page.totalPages.toString())
return page
}
private fun PageImpl<*>.links(request: ServerHttpRequest): String {
val links = mutableListOf<String>()
val builder = UriComponentsBuilder.fromUri(request.uri)
if (request.uri.host == "localhost") {
builder.port(request.uri.port)
}
if (!this.isFirst) {
val link = builder.replacePageAndSize(this.pageable.first())
links.add("<${link.toUriString()}>; rel=\"first\"")
}
if (this.hasPrevious()) {
val link = builder.replacePageAndSize(this.previousPageable())
links.add("<${link.toUriString()}>; rel=\"prev\"")
}
if (this.hasNext()) {
val link = builder.replacePageAndSize(this.nextPageable())
links.add("<${link.toUriString()}>; rel=\"next\"")
}
if (!this.isLast) {
val last = builder.cloneBuilder()
last.replaceQueryParam("page", this.totalPages)
last.replaceQueryParam("size", this.size)
links.add("<${last.toUriString()}>; rel=\"last\"")
}
return links.joinToString(",")
}
private fun UriComponentsBuilder.replacePageAndSize(
page: Pageable
): UriComponentsBuilder {
val builder = this.cloneBuilder()
val pageNumber = if (oneIndexed)
page.pageNumber.plus(1)
else
page.pageNumber
builder.replaceQueryParam("page", pageNumber)
builder.replaceQueryParam("size", page.pageSize)
return builder
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.