JSON レスポンスアドバイス

REST コントローラーのオブジェクトバインディングや、エラーハンドリングに特化したアドバイスを作成するには、通常のコントローラー同様、@ControllerAdvice を使用しますが、org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice を継承する点が異なります。 これがどのように動作するのか確認するため、ここまでに作成したコントローラーに、呼びだされた時間を付与するコントローラーアドバイスを、以下を参考に作成します。

// src/main/java/io/github/yo1000/sss/aspect/AppendTimeJacksonControllerAdvice.java
package io.github.yo1000.sss.aspect;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class AppendTimeJacksonControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {
    private static final Logger LOGGER = LoggerFactory.getLogger(AppendTimeJacksonControllerAdvice.class);

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue mappingJacksonValue, MediaType mediaType,
            MethodParameter methodParameter, ServerHttpRequest serverHttpRequest,
            ServerHttpResponse serverHttpResponse) {}

    @Override
    protected MappingJacksonValue getOrCreateContainer(Object body) {
        MappingJacksonValue jacksonValue = super.getOrCreateContainer(body);
        Object bodyValue = jacksonValue.getValue();
        LOGGER.info("{}", bodyValue);

        Map<String, Object> wrappedMap = new HashMap<>();
        wrappedMap.put("body", bodyValue);
        wrappedMap.put("time", System.currentTimeMillis());

        jacksonValue.setValue(wrappedMap);
        return jacksonValue;
    }
}

アプリケーションを再起動して、cURL で、http://localhost:8080/api/v1/memo をリクエストしてみます。

$ curl -v -X GET http://localhost:8080/api/v1/memo
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/v1/memo HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Wed, 20 Jul 2016 04:42:06 GMT
<
* Connection #0 to host localhost left intact
{"time":1468989726732,"body":[{"memo":"Springを学ぶ","author":"金次郎","created":null},{"memo":"Thymeleafを学ぶ","author":"金次郎","created":null},{"memo":"Flywayを学ぶ","author":"金次郎","created":null},{"memo":"AspectJを生麩","author":"金字塔","created":null},{"memo":"REST対応する","author":"銀河万丈","created":null}]}

コントローラーアドバイスで設定したように、"time":1468989726732 がレスポンスに含まれるようになったことを確認できました。

このように、REST コントローラーに対するアドバイスについては、@ControllerAdvice だけでなく、AbstractMappingJacksonResponseBodyAdvice クラスの継承も必要になってきます。

この他にもレスポンスの内容に応じて、いくつかの抽象クラスが org.springframework.web.servlet.mvc.method.annotation パッケージ配下に用意されているので、必要に応じてリファレンスを確認してみると良いでしょう。

サンプル

ここまでのコードサンプルは、以下より入手できます。

$ git clone -b appendix/2 https://github.com/yo1000/self-study-spring.git

results matching ""

    No results matching ""