Spring WebMVCをやってみる (10) - gsonを使う -

2013-12-15T00:00:00+00:00 Java Spring Framework JavaScript

例えば

  • @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);
    }
  });
});

多分前回とパラメーターをちょっと足しただけでほとんど変わってない

java.util.ServiceLoader Spring WebMVCをやってみる (9) - @ExceptionHandler -