LoginSignup
1
0

More than 5 years have passed since last update.

Ratpack入門 (5) - Json & Registry

Last updated at Posted at 2018-01-19

Ratpack入門シリーズ

  1. Ratpack入門 (1) - Ratpackとは
  2. Ratpack入門 (2) - アーキテクチャー
  3. Ratpack入門 (3) - hello world 詳解
  4. Ratpack入門 (4) - ルーティング & 静的コンテンツ
  5. Ratpack入門 (5) - Json & Registry
  6. Ratpack入門 (6) - Promise
  7. Ratpack入門 (7) - Guice & Spring
  8. Ratpack入門 (8) - セッション
  9. Ratpack入門 (9) - Thymeleaf

Json

RatpackはJacksonを使った組み込みのJSONサポート機能を持っています。JSONは標準で利用でき、ratpack-jacksonモジュールを別途追加する必要はありません。

公式

@Data
@AllArgsConstructor
@NoArgsConstructor
public static final class User {
    private int id;
    private String name;
}

public static void main( String[] args ) throws Exception {

    Action<Chain> handlers = chain -> {
        chain.all( ctx -> {
            ctx.byMethod( m -> {
                m.get( iCtx -> iCtx.render( Jackson.json( new User( 123, "John" ) ) ) );
                m.post( iCtx -> iCtx.parse( Jackson.fromJson( User.class ) ).then( user -> {
                    iCtx.render( "Received user: " + user );
                } ) );
                // m.post( iCtx -> iCtx.parse( User.class ).then( user -> {
                //     iCtx.render( "Received user: " + user );
                // } ) );
            } );
        } );
    };

    RatpackServer.start( server -> server
            .serverConfig( ServerConfig.builder().development( true ).build() )
            .handlers( handlers ) );
}

(lombokを使っています)

使い方は簡単で、ratpack.jackson.Jacksonの各メソッドでラップしてあげるだけです。Jacksonを通して、オブジェクトをマーシャル/アンマーシャルしてくれます。Jackson.fromJson()は省略でき、コメントアウトされている部分で代替できます。これは、生のClassが渡されたとき用のNoOptParserSupportが自動的に追加されているためです。

RegistryRendererParserの関係

render()Objectを引数にとりますし、parse()は任意のClassを引数にとります。このとき、Objectから適切なHTTPレスポンスへ変換される必要があります。以前にも少し述べたように、この変換にはRegistryに登録されたRendererクラス、Parserクラスが、その要求された方に応じて呼び出されることで、適切に変換されます。ですから、自分で設定したカスタムレンダラーをレジストリに登録することで、独自の変換を実現できます。

ここでは例として、CSVでデータを受け取ったり返却したりする仕組みを作ってみましょう。Userは上記の例と同じです。

まず、Rendererを定義します。

public static final class UserRenderer extends RendererSupport<User> {
    @Override
    public void render( Context ctx, User user ) throws Exception {
        ctx.getResponse().contentType( "text/plain" );
        ctx.render( user.getId() + "," + user.getName() );
    }
}

RenderインターフェースのスケルトンであるRendererSupportを継承します。ハンドラー内のContext.render()Userを引数として呼ばれると、Userクラスを出力できる、このレンダラーが呼ばれます。引数はそのリクエストのContextと、render()に渡されたUserです。大体想像通りかと思います。レスポンスの出力方法は、ハンドラーでやっていた時と同様です。

public static final class UserParser extends NoOptParserSupport {
    @Override
    protected <T> T parse( Context context, TypedData requestBody, TypeToken<T> type ) throws Exception {
        if ( type.getRawType().equals( User.class ) ) {
            String[] csv = requestBody.getText().split( "," );
            User user = new User( Integer.parseInt( csv[0] ), csv[1] );
            return Types.cast( user );
        } else {
            return null;
        }
    }
}

レンダラーと同様、パーサーもParserインターフェースを実装するNoOptParserSupportスケルトンを継承します。NoOptParserSupportは、特にparse(Class)が呼ばれたときのための、最も簡単なパーサーを提供するために使います。TypeToken、すなわち要求されている方がUserでなかった場合、このパーサーでは処理できないことを示すためnullを返します。

public static final class CustomHandler implements Handler {
    @Override
    public void handle( Context ctx ) throws Exception {
        ctx.byMethod( m -> {
            m.get( iCtx -> {
                iCtx.render( new User( 123, "John" ) );
            } );
            m.post( iCtx -> {
                iCtx.parse( User.class ).then( user -> {
                    iCtx.render( "Received: " + user );
                } );
            } );
        } );
    }
}

レジストリに登録する場合の動きを示すため、ここではハンドラーも独立したクラスにしてみました。

public static void main( String[] args ) throws Exception {
    RatpackServer.start( server -> server
            .serverConfig( ServerConfig.builder().development( true ) )
            .registryOf( spec -> {
                spec.add( new UserRenderer() )
                    .add( new UserParser() )
                    .add( new CustomHandler() );
            } )
            .handler( CustomHandler.class ) );
}

Registryに、作成した独自クラスを登録してあげる必要があります。handler()メソッドに渡しているのがインスタンスではなくClassなのに注目してください。

実行結果

$ curl localhost:5050 && echo
123,John
$ curl -X POST localhost:5050 --data "456,Jack" && echo
Received: Sample5_2_Registry.User(id=456, name=Jack)

CSVが独自定義のレンダラー、パーサーを通して処理されているのが確認できました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0