Spring MVC 4.1から対応したGoogleのProtocolBuffers対応を試してみます。(4.1.0だとバグって動きません)
Spring 4.1に対応した、Spring Boot 1.2(M2)を使っています。
Protoファイルはチュートリアルのものを使います。
Controllerの書き方はJSONというかRESTの場合と同じです。
package com.example.tutorial;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AddressBookController {
@RequestMapping("person")
AddressBookProtos.Person person() {
return AddressBookProtos.Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.addPhone(
AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(AddressBookProtos.Person.PhoneType.HOME))
.build();
}
}
Spring側の対応としては、@RestController
を使ったときに通るorg.springframework.http.converter.HttpMessageConverter
のProtobuf実装(org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter
)を追加しています。
JavaConfigにこのHttpMessageConverter
を追加します。
package com.example.tutorial;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
@Configuration
public class AppConfig {
@Bean
ProtobufHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufHttpMessageConverter((extensionRegistry) -> {
});
}
}
あとはいつも通りSpring Bootのエントリポイントを作るだけ
package com.example.tutorial;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
@ComponentScan
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
このApp
を実行して、アクセスすると
$ curl http://localhost:8080/person
John Doe� jdoe@example.com"
555-4321
こんな感じです。
テストはこんな感じ。(RestTemplate
使うので十分だった・・)
package com.example.tutorial;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.ResponseBodyData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static com.jayway.restassured.RestAssured.when;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
@WebAppConfiguration
@IntegrationTest({"server.port:0"})
public class AddressBookControllerTest {
@Value("${local.server.port}")
int port;
@Before
public void setUp() throws Exception {
RestAssured.port = port;
}
@Test
public void testPerson() throws Exception {
ResponseBodyData body = when().get("/person")
.then()
.extract()
.body();
AddressBookProtos.Person person = AddressBookProtos.Person.parseFrom(body.asInputStream());
assertThat(person.getName(), is("John Doe"));
assertThat(person.getId(), is(1234));
assertThat(person.getEmail(), is("jdoe@example.com"));
assertThat(person.getPhoneList(), hasSize(1));
assertThat(person.getPhone(0).getNumber(), is("555-4321"));
assertThat(person.getPhone(0).getType(), is(AddressBookProtos.Person.PhoneType.HOME));
}
}
せっかくなのでPythonからもアクセスしてみます。
import httplib
import addressbook_pb2
conn = httplib.HTTPConnection("localhost", 8080)
conn.request("GET", "/person")
res = conn.getresponse()
body = res.read()
conn.close()
person = addressbook_pb2.Person()
person.ParseFromString(body)
print person
Python2系ですいません・・
$ python test.py
name: "John Doe"
id: 1234
email: "jdoe@example.com"
phone {
number: "555-4321"
type: HOME
}
いけました。
HTTP経由だとJSONに比べてどのくらいメリットあるんだろう・・・
ソースコードはGithubに上げました。