Spring WebMVCをやってみる (10) - gsonを使う -
例えば
- @ResponseBodyアノテーションがついてるメソッドでStringじゃなくてオブジェクトだとかをぶん投げたりとか
- @RequestBodyアノテーションがついてるメソッドの引数でStringじゃなくてオブジェクトだとかを指定したりとか
っていうケースな場合にJSONで投げられる事を前提とするのであれば、MessageConverterな方式を利用する事でサポート出来るらしいのでやってみた
以前の補足
Spring WebMVCをやってみる (7) – @RequestBody -でも@RequestBodyでJSONなStringからデコードするのはやったけれども、補足なポイントはそこではなくて現在なSpring WebMVCではheadersを指定せずにconsumes/producesを指定するのが今時な方法らしい
一応補足しておく。今回でも使うので
Spring WebMVCでgsonでエンコード/デコードを出来る仕組みを利用できるようにする
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="sample" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean
p:supportedMediaTypes="text/plain"
class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean
p:supportedMediaTypes="application/json"
class="org.springframework.http.converter.json.GsonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 以降な設定は省略 -->
</beans>
っていう設定をしないといけない模様なんですけど、org.springframework.http.converter.json.GsonHttpMessageConverter自体はSpringにもSpring WebMVC等にも含まれていなくて、なぜかspring-androidに含まれている。まぁこれだけの為にspring-androidなライブラリ参照を入れるのもどうかと思うので、https://raw.github.com/spring-projects/spring-android/master/spring-android-rest-template/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.javaとかからダウンロードしてきてソースぶち込んじゃえば良い
んまぁ当たり前だけどgsonなライブラリ参照も無いとダメなので
repositories {
mavenCentral()
}
dependencies {
compile "javax.servlet:servlet-api:2.5"
compile "org.springframework:spring-webmvc:3.2.5.RELEASE"
// 追加
compile "com.google.code.gson:gson:2.2.4"
testRuntime "javax.servlet:jstl:1.2"
testCompile "junit:junit:4.11"
testCompile "org.hamcrest:hamcrest-all:1.3"
testCompile "org.springframework:spring-test:3.2.5.RELEASE"
}
とかあたり追加しておく。Eclipseとかで開発しているならeclipseタスクで依存性関係設定を反映させておく
まぁあとはコントローラーとそのテストを書くだけ
SampleController.java
package sample;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/sample")
public class SampleController {
@RequestMapping(
method = RequestMethod.POST,
consumes = "application/json",
produces = "application/json"
)
@ResponseBody
public List<Sample> index(@RequestBody Sample sample) {
return Arrays.asList(sample);
}
// 普通に@ResponseBodyでStringを返すのも定義しておく。テスト要件の為
@RequestMapping("/test")
@ResponseBody
public String test() {
return "hoge";
}
}
見て分かるようにindexメソッドでは@ResponseBodyついてるけど返り値がStringでな無いし、@RequestBodyが引数についてるけどStringでは無い。そこは先ほどのgsonなMessage Converterな仕組みが動いてくれる模様
SampleControllerTest.java
package swmvc;
import java.lang.reflect.Type;
import java.util.List;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MvcResult;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import sample.Sample;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
public class SampleControllerTest extends AbstractTestCase {
private Gson gson = new Gson();
@Test
public void test_index() throws Exception {
Sample sample = new Sample();
sample.setId(1);
sample.setName("hoge");
MvcResult result = mock.perform(
post("/sample")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(gson.toJson(sample))
)
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
.andReturn();
MockHttpServletResponse response = result.getResponse();
assertThat(response, notNullValue());
Type type = new TypeToken<List<Sample>>(){}.getType();
List<Sample> decodeObjs = gson.fromJson(response.getContentAsString(), type);
assertThat(decodeObjs, notNullValue());
assertThat(decodeObjs, hasSize(1));
Sample decodeObj = decodeObjs.get(0);
assertThat(decodeObj, notNullValue());
assertThat(decodeObj.getId(), is(sample.getId()));
assertThat(decodeObj.getName(), is(sample.getName()));
}
@Test
public void test_test() throws Exception {
mock.perform(get("/sample/test"))
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain"))
.andExpect(content().string("hoge"));
}
}
っていう感じでテスト書いて実行してみるとか。
JavaScriptでゴニョゴニョする場合は?
$(document).ready(function() {
$.ajax({
type: "POST",
url: "/swmvc/sample.action",
dataType: "json",
data: JSON.stringify({ "id": 1, "name": "hoge" }),
contentType: "application/json",
success: function(data) {
console.log(data);
}
});
});
多分前回とパラメーターをちょっと足しただけでほとんど変わってない