8

I'm trying to pass a protobuf parameter to a REST endpoint but I get

org.springframework.web.client.HttpServerErrorException: 500 null

each time I try. What I have now is something like this:

@RestController
public class TestTaskEndpoint {

    @PostMapping(value = "/testTask", consumes = "application/x-protobuf", produces = "application/x-protobuf")
    TestTaskComplete processTestTask(TestTask testTask) {
        // TestTask is a generated protobuf class
        return generateResult(testTask);
    }
}


@Configuration
public class AppConfiguration {

    @Bean
    ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }

}

@SpringBootApplication
public class JavaConnectorApplication {

    public static void main(String[] args) {
        SpringApplication.run(JavaConnectorApplication.class, args);
    }
}

and my test looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@WebAppConfiguration
public class JavaConnectorApplicationTest {


    @Configuration
    public static class RestClientConfiguration {

        @Bean
        RestTemplate restTemplate(ProtobufHttpMessageConverter hmc) {
            return new RestTemplate(Arrays.asList(hmc));
        }

        @Bean
        ProtobufHttpMessageConverter protobufHttpMessageConverter() {
            return new ProtobufHttpMessageConverter();
        }
    }

    @Autowired
    private RestTemplate restTemplate;

    private int port = 8081;

    @Test
    public void contextLoaded() {

        TestTask testTask = generateTestTask();

        final String url = "http://127.0.0.1:" + port + "/testTask/";
        ResponseEntity<TestTaskComplete> customer = restTemplate.postForEntity(url, testTask, TestTaskComplete.class);

        // ...

    }
}

I'm sure that it is something with the parameters because if I create a variant which does not take a protobuf parameter but returns one it just works fine. I tried debugging the controller code but the execution does not reach the method so the problem is probably somewhere else. How do I correctly parametrize this REST method?

Adam Arold
  • 29,285
  • 22
  • 112
  • 207
  • I have the exact same question. When i look at the issue, i see that its failing while invoking Proto constructor. The relavant log is here : Failed to instantiate [com.welflex.provider.CustomerProtos$Customer]: No default constructor found; – Harry Jun 15 '17 at 09:29
  • E ended up consuming `String`s which I then converted by hand to protobuf objects. – Adam Arold Jun 15 '17 at 16:26
  • I tried and it worked. Thanks for your help. – Harry Jun 16 '17 at 05:39
  • Hi Adam, does it work well and provide any speed improvement or such? Thanks for sharing. – jumping_monkey Jan 27 '23 at 00:25

2 Answers2

1

This is my first stack overflow answer but I was a lot to frustred from searching for working examples with protobuf over http and spring.

the answer https://stackoverflow.com/a/44592469/15705964 from Jorge is nearly correct. Like the comments mention: "This won't work in itself. You need to add a converter somewhere at least."

Do it like this:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    ProtobufHttpMessageConverter protobufHttpMessageConverter;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(protobufHttpMessageConverter);
    }

}

The ProtobufHttpMessageConverter will do his job automatically and add the object to your controller methode

@RestController
public class ProtobufController {

    @PostMapping(consumes = "application/x-protobuf", produces = "application/x-protobuf")
    public ResponseEntity<TestMessage.Response> handlePost(@RequestBody TestMessage.Request protobuf) {

    TestMessage.Response response = TestMessage.Response.newBuilder().setQuery("This is a protobuf server Response")
            .build();

    return ResponseEntity.ok(response);
}

Working example with send and reseive with rest take a look: https://github.com/Chriz42/spring-boot_protobuf_example

0

Here it's the complete answer

@SpringBootApplication
public class JavaConnectorApplication {

    public static void main(String[] args) {
        SpringApplication.run(JavaConnectorApplication.class, args);
    }
}

Then you need to provide the right configuration.

 @Configuration
    public class AppConfiguration {

         //You need to add in this list all the messageConverters you will use
         @Bean 
        RestTemplate restTemplate(ProtobufHttpMessageConverter hmc) {
            return new RestTemplate(Arrays.asList(hmc,smc));
        }

        @Bean
        ProtobufHttpMessageConverter protobufHttpMessageConverter() {
            return new ProtobufHttpMessageConverter();
        }

    }

And finally your RestController.

@RestController
public class TestTaskEndpoint {

    @PostMapping(value = "/testTask")
    TestTaskComplete processTestTask(@RequestBody TestTask testTask) {
        // TestTask is a generated protobuf class
        return generateResult(testTask);
    }
}

The @RequestBody annotation: The body of the request is passed through an HttpMessageConverter (That you already defined) to resolve the method argument depending on the content type of the request

And your test class:

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class JavaConnectorApplicationTest {

    @Autowired
    private RestTemplate restTemplate;

    private int port = 8081;

    @Test
    public void contextLoaded() {

        TestTask testTask = generateTestTask();

        final String url = "http://127.0.0.1:" + port + "/testTask/";
        ResponseEntity<TestTaskComplete> customer = restTemplate.postForEntity(url, testTask, TestTaskComplete.class);

        // Assert.assertEquals("dummyData", customer.getBody().getDummyData());

    }
}
  • This won't work in itself. You need to add a converter somewhere at least. – Adam Arold Jun 17 '17 at 18:33
  • Btw i tried the code from this excellent article by Piotr Mińkowski : https://piotrminkowski.wordpress.com/2017/06/05/exposing-microservices-over-rest-protocol-buffers/ . The modified source code link is here : https://github.com/harryalto/sample-microservices-protobuf. Btw it uses the same logic as mentioned by Adam Aroid above i.e registering the protobufHttpMessageConverter – Harry Jun 18 '17 at 09:18
  • This answer has nothing to consume as protobuff object. It won't work. – Prasath Dec 03 '18 at 19:18