title: Resolving Pageable in Webflux
body: <p>I spent a lot of time to find a solution about Pageable in Webflux, unfortunately, at the time of writing this, Webflux does not support Pageable so I came up with a solution and this is what I have implemented. It is a hacky solution to resolve Pageable and Sort type in Webflux controller/resource in Spring Boot.</p>
  
  <p>Here is the solution: (I know it might be ugly but you can use it till Spring team fix the problem, there are on-going efforts for that. <a href="https://jira.spring.io/browse/DATACMNS-1211" rel="nofollow noreferrer">Spring Jira</a></p>
  
  <p>Also, the gist is here: <a href="https://gist.github.com/ghahramani/12694474d300b72bb5c7f8bd6e61beb8" rel="nofollow noreferrer">Github</a></p>
  
  <p>Update 1: You can find the article <a href="https://medium.com/@ghahremani/pageable-and-sort-in-webflux-c6f6c598537a" rel="nofollow noreferrer">here in medium</a> as well</p>
  
  <p>Versions:</p>
  
  <pre><code>Spring Boot: 2.0.2.RELEASE
  Gradle Kotlin: 1.2.41
  </code></pre>
  
  <p>Feel free to give me hint to improve it. (Code is Kotlin)</p>
  
  <pre><code>@Configuration
  class PageableSerializer {
  
  @Bean
  @ConditionalOnMissingBean
  fun pageableCustomizer(properties: SpringDataWebProperties): PageableHandlerMethodArgumentResolverCustomizer {
      return PageableHandlerMethodArgumentResolverCustomizer { resolver -&gt;
          val pageable = properties.pageable
  
          resolver.setPageParameterName(pageable.pageParameter)
          resolver.setSizeParameterName(pageable.sizeParameter)
          resolver.setOneIndexedParameters(pageable.isOneIndexedParameters)
          resolver.setPrefix(pageable.prefix)
          resolver.setQualifierDelimiter(pageable.qualifierDelimiter)
          resolver.setFallbackPageable(PageRequest.of(0, pageable.defaultPageSize))
          resolver.setMaxPageSize(pageable.maxPageSize)
  
      }
  }
  
  @Bean
  @ConditionalOnMissingBean
  fun sortCustomizer(properties: SpringDataWebProperties): SortHandlerMethodArgumentResolverCustomizer {
      return SortHandlerMethodArgumentResolverCustomizer { resolver -&gt;
          resolver.setSortParameter(properties.sort.sortParameter)
      }
  }
  
  @Bean
  fun pageableHandler(pageableResolver: Optional&lt;PageableHandlerMethodArgumentResolverCustomizer&gt;, sortHandler: SortHandlerMethodArgumentResolver, reactiveAdapterRegistry: ReactiveAdapterRegistry): PageableHandlerMethodArgumentResolver {
      val handler = PageableHandlerMethodArgumentResolver(sortHandler)
      pageableResolver.ifPresent { c -&gt; c.customize(handler) }
      return handler
  }
  
  @Bean
  fun sortHandler(sortResolver: Optional&lt;SortHandlerMethodArgumentResolverCustomizer&gt;): SortHandlerMethodArgumentResolver {
      val handler = SortHandlerMethodArgumentResolver()
      sortResolver.ifPresent { c -&gt; c.customize(handler) }
      return handler
  }
  
  }
  </code></pre>
  
  <p>And to register handler:</p>
  
  <pre><code>@Component
  class PageableResolver(registry: ReactiveAdapterRegistry, private val resolver: PageableHandlerMethodArgumentResolver) : HandlerMethodArgumentResolverSupport(registry) {
  
      override fun resolveArgument(parameter: MethodParameter, bindingContext: BindingContext, exchange: ServerWebExchange): Mono&lt;Any&gt; {
          return Mono.just(resolver.resolveArgument(parameter, null, MockNative(exchange.request.queryParams), null))
      }
  
      override fun supportsParameter(parameter: MethodParameter): Boolean {
          return this.resolver.supportsParameter(parameter)
      }
  
      private class MockNative(private val parameters: MultiValueMap&lt;String, String&gt;) : NativeWebRequest {
          override fun getParameter(paramName: String): String? {
              return this.parameters.getFirst(paramName)
          }
  
          override fun getParameterValues(paramName: String): Array&lt;String&gt;? {
              return this.parameters[paramName]?.toTypedArray()
          }
  
          override fun isUserInRole(role: String): Boolean {
              return false
          }
  
          override fun getRemoteUser(): String? {
              return null
          }
  
          override fun getLocale(): Locale {
              return Locale.getDefault()
          }
  
          override fun getParameterMap(): MutableMap&lt;String, Array&lt;String&gt;&gt; {
              return mutableMapOf()
          }
  
          override fun getSessionId(): String {
              return ""
          }
  
          override fun getAttributeNames(scope: Int): Array&lt;String&gt; {
              return arrayOf()
          }
  
          override fun registerDestructionCallback(name: String, callback: Runnable, scope: Int) {
  
          }
  
          override fun resolveReference(key: String): Any? {
              return null
          }
  
          override fun getHeaderValues(headerName: String): Array&lt;String&gt;? {
              return null
          }
  
          override fun getUserPrincipal(): Principal? {
              return null
          }
  
          override fun getDescription(includeClientInfo: Boolean): String {
              return ""
          }
  
          override fun getSessionMutex(): Any {
              return ""
          }
  
          override fun getNativeResponse(): Any? {
              return null
          }
  
          override fun &lt;T : Any?&gt; getNativeResponse(requiredType: Class&lt;T&gt;?): T? {
              return null
          }
  
          override fun getParameterNames(): MutableIterator&lt;String&gt; {
              return mutableListOf&lt;String&gt;().iterator()
          }
  
          override fun getNativeRequest(): Any {
              return ""
          }
  
          override fun &lt;T : Any?&gt; getNativeRequest(requiredType: Class&lt;T&gt;?): T? {
              return null
          }
  
          override fun removeAttribute(name: String, scope: Int) {
  
          }
  
          override fun getHeader(headerName: String): String? {
              return null
          }
  
          override fun getContextPath(): String {
              return ""
          }
  
          override fun checkNotModified(lastModifiedTimestamp: Long): Boolean {
              return false
          }
  
          override fun checkNotModified(etag: String): Boolean {
              return false
          }
  
          override fun checkNotModified(etag: String?, lastModifiedTimestamp: Long): Boolean {
              return false
          }
  
          override fun getHeaderNames(): MutableIterator&lt;String&gt; {
              return mutableListOf&lt;String&gt;().iterator()
          }
  
          override fun getAttribute(name: String, scope: Int): Any? {
              return null
          }
  
          override fun setAttribute(name: String, value: Any, scope: Int) {
          }
  
          override fun isSecure(): Boolean {
              return false
          }
      }
  
  }
  </code></pre>