Spring Error loading using Netynet AsyncRestTemplate client

I have a Spring Boot 1.3.6 application built out of the box and using the built-in Tomcat server. An application has one endpoint that performs a very simple ping.

Later, I determined the appropriate client that calls this simple endpoint using AsyncRestTemplate , however, if my client uses Netty4ClientHttpRequestFactory , the request fails, otherwise it will succeed.

My example is below in Kotlin, but it does not work exactly the same in Java, so it is not related to the language that I use to implement it.

Server

 @SpringBootApplication open class EchoApplication { companion object { @JvmStatic fun main(args: Array<String>) { SpringApplication.run(EchoApplication::class.java, *args) } } @Bean open fun objectMapper(): ObjectMapper { return ObjectMapper().apply { dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") registerModule(KotlinModule()) } } @Bean open fun customConverters(): HttpMessageConverters { return HttpMessageConverters(listOf(MappingJackson2HttpMessageConverter(objectMapper()))) } } 

My endpoint is as follows:

 @RestController class EchoController { @RequestMapping(value="/foo", method = arrayOf(RequestMethod.PUT)) fun echo(@RequestBody order: Order): Order { return order } } 

And the order data class

 data class Order(val orderId: String) 

Note. Since I use Kotlin, I also added the Kotlin Jackson Module to ensure proper deserialization of the constructor.

Client

Then I set about creating a client that invokes this endpoint.

If in my client I do something like the following: it works fine and I get a successful echo reply.

  val executor = TaskExecutorAdapter(Executors.newFixedThreadPool(2)) val restTemplate = AsyncRestTemplate(executor) restTemplate.messageConverters = listOf(MappingJackson2HttpMessageConverter(mapper)) val promise = restTemplate.exchange(URI.create("http://localhost:8080/foo"), HttpMethod.PUT, HttpEntity(Order("b1254"), headers), Order::class.java) promise.addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> { override fun onSuccess(result: ResponseEntity<Order>) { println(result.body) } override fun onFailure(ex: Throwable) { ex.printStackTrace() if(ex is HttpStatusCodeException){ println(ex.responseBodyAsString) } } }) 

As mentioned above, the above code works fine and prints a successful echo response.

Problem

But if I decide to use the Netty client, then I get 400 Bad Request reports that I did not send:

 val nettyFactory = Netty4ClientHttpRequestFactory() val restTemplate = AsyncRestTemplate(nettyFactory) 

When I do this, I get an HttpMessageNotReadableException message with the message "Required request body missing."

I was debugging Spring boot code, and I see that when reading ServletInputStream it always returns -1, as if it were empty.

In my gradle, I added runtime('io.netty:netty-all:4.1.2.Final') , so I use what is currently the latest version of Netty. This version of Netty works great when interacting with endpoints in other projects that I have that use regular Spring (i.e. Not Spring Boot).

How did it SimpleClientHttpRequestFactory that SimpleClientHttpRequestFactory works fine, but Netty4ClientHttpRequestFactory fails?

I thought this might be due to the Tomcat embedded server, however, if I pack this application as a war and deploy it to an existing Tomcat server (i.e. without using the built-in), the problem persists. So, I assume this is related to Spring / Spring Boot.

Am I missing any configuration in my Spring boot application? Any suggestions on how to get the Netty client to work with Spring Boot?

+7
java spring spring-boot kotlin netty
source share
2 answers

The problem seems to be related to client side serialization. Since this code works fine:

 restTemplate.exchange( URI.create("http://localhost:8080/foo"), HttpMethod.PUT, HttpEntity("""{"orderId":"1234"}""", HttpHeaders().apply { setContentType(MediaType.APPLICATION_JSON); }), Order::class.java ).addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> { override fun onSuccess(result: ResponseEntity<Order>) { println("Result: ${result.body}") } override fun onFailure(ex: Throwable) { ex.printStackTrace() if (ex is HttpStatusCodeException) { println(ex.responseBodyAsString) } } }) 

I need a more accurate look at restTemplate for its transformers, but now you can write this part as follows:

 val mapper = ObjectMapper() restTemplate.exchange( URI.create("http://localhost:8080/foo"), HttpMethod.PUT, HttpEntity(mapper.writeValueAsString(Order("HELLO")), HttpHeaders().apply { setContentType(MediaType.APPLICATION_JSON); }), Order::class.java ).addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> { override fun onSuccess(result: ResponseEntity<Order>) { println("Result: ${result.body}") } override fun onFailure(ex: Throwable) { ex.printStackTrace() if (ex is HttpStatusCodeException) { println(ex.responseBodyAsString) } } }) 

As you can see, I do not use KotlinModule, and this code works fine, so there is clearly a problem in the AsyncRestTemplate configuration.

+2
source share

My 2 cents. This is definitely not a solution.

I configured asyncRestTemplate using AsyncHttpClientRequestInterceptor and it worked magically. No explanation, period!

 public class AsyncClientLoggingInterceptor implements AsyncClientHttpRequestInterceptor { @Override public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException { return execution.executeAsync(request, body); } } 
+1
source share

All Articles