ヒトリ歩き

愚痴とかいろいろ書きます

RequestBodyの内容によって使用するリソースを使い分ける

Springを使うと@RequestBodyを使えばリクエストボディのjsonJavaオブジェクトに変換してくれるのだが、URIが一緒だけどリクエストボディの内容が違う場合はどうすればいいのか?

Springを使っているのでフレームワークの恩恵をしっかり受けるべきなのだろうが、URIは一緒でもリクエストボディが違うということが発生したらサーバサイドでなんとかしなければいけない。

そんな面倒なことが起きたら対応できるのか?ちょっと調べてみたところ、@RequestBodyをMapで指定してやるとキー、バリューでリクエストボディがMapオブジェクトに格納されることが分かったのでやってみた。

やってみること

URIは同じだが、リクエストボディが異なるものをPOSTリクエストする。
ただし、typeというデータは共通で持たせてtypeの値で違いを判断する。
今回は、typeにconcertとtripというものを用意。

{
  "type":"concert",
  "ticketid": "111",
  "concertname":"oooomede",
  "name":"oredesuyo"
}
{
  "type":"trip",
  "id": "aabbcc-111",
  "name":"oredesu sasaki",
  "date":"2020/01/01 00:00:00",
  "seat":"D3"
}

コントローラの実装

コントローラの実装は、@RequestBodyにMap<String, Object>型を指定する。
Mapを指定することで、リクエストボディがキー、バリュー形式で格納される。

    @RequestMapping(method = RequestMethod.POST)
    public BookResponse getBook(@RequestBody Map<String, Object> bookingRequest) {

typeキーの値を見て、どの種類の要求なのかを判断してリクエストに対応するクラスに変換する。
変換にはJacksonのObjectMapperクラスを使用して変換する。
ここでは対応するクラスのオブジェクトに変換して標準出力して終わりにしている。

        String type = (String) bookingRequest.get("type");
        ObjectMapper mapper = new ObjectMapper();
        switch(type) {
            case "concert":
                ConcertResource concert =
                    mapper.convertValue(bookingRequest, ConcertResource.class);
                System.out.println(concert.toString());
                break;
            case "trip":
                TripResource trip =
                    mapper.convertValue(bookingRequest, TripResource.class);
                System.out.println(trip.toString());
                break;
        }

リクエストを投げてみる

concertのリクエストを投げてみる。

$ curl -X POST -H "Content-Type: application/json" -d '{"type":"concert", "ticketid": "111", "concertname":"oooomede", "name":"oredesuyo"}' http://localhost:8080/book

結果の標準出力

ConcertResource(type=concert, ticketid=111, concertname=oooomede, name=oredesuyo)

tripのリクエストを投げてみる。

$ curl -X POST -H "Content-Type: application/json" -d '{"type":"trip", "id": "aabbcc-111", "name":"oredesu sasaki", "date":"2020/01/01 00:00:00", "seat":"D3"}' http://localhost:8080/book

結果の標準出力

TripResource(type=trip, id=aabbcc-111, name=oredesu sasaki, date=2020/01/01 00:00:00, seat=D3)

ちゃんと対応するクラスに変換することが出来た。

最後に

本来であれば用途によってURIを変更して対応すべきとは思うが、何かしらの要因でURIが同じでもリクエストボディの内容が違うケースが発生した場合に対応は可能であることが分かった。
他に良い方法がないかもう少し調べてみようと思う。 本当にフレームワークの恩恵はしっかり受けたいものだな。

package com.example.demo.book;
import java.util.Map;
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.RestController;
import com.example.demo.book.resource.BookResponse;
import com.example.demo.book.resource.ConcertResource;
import com.example.demo.book.resource.TripResource;
import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
@RequestMapping("book")
public class BookController {

    @RequestMapping(method = RequestMethod.POST)
    public BookResponse getBook(@RequestBody Map<String, Object> bookingRequest) {

       // typeを見て変換するクラスを判断する
        String type = (String) bookingRequest.get("type");
        ObjectMapper mapper = new ObjectMapper();
        switch(type) {
            case "concert":
                ConcertResource concert =
                    mapper.convertValue(bookingRequest, ConcertResource.class);
                System.out.println(concert.toString());
                break;
            case "trip":
                TripResource trip =
                    mapper.convertValue(bookingRequest, TripResource.class);
                System.out.println(trip.toString());
                break;
        }

        BookResponse response = new BookResponse();
        return response;

    }

}
package com.example.demo.book.resource;

import java.io.Serializable;

import lombok.Data;

@Data
public class ConcertResource implements Serializable {

    private String type;
    private String ticketid;
    private String concertname;
    private String name;
}
package com.example.demo.book.resource;

import java.io.Serializable;

import lombok.Data;

@Data
public class TripResource implements Serializable {

    private String type;
    private String id;
    private String name;
    private String date;
    private String seat;
}
package com.example.demo.book.resource;

import lombok.Data;

@Data
public class BookResponse {

    private String status = "ok";
    private String errorCode = "0";

}

参考

stackoverflow.com