intra-mart Accel Platform TERASOLUNA Server Framework for Java (5.x) プログラミングガイド 第15版 2019-08-01

RESTful Web Service

概要

ここでは intra-mart Accel Platform 上で TERASOLUNA Server Framework for Java (5.x) のREST APIの実装方法について説明します。

TERASOLUNA Server Framework for Java (5.x) でREST APIを作成する場合には、@RestControllerアノテーションを付けたControllerクラスを作ります。 intra-mart Accel Platform ではControllerクラスの替わりに Web API Maker を使います。 Web API Maker のサービスクラスを intra-mart Accel Platform のControllerクラスに対応させ、 intra-mart Accel Platform のServiceクラスを呼出しビジネスロジックを実行します。

Web API Maker についての詳細は、 「Web API Maker プログラミングガイド」を参照してください。

REST APIの作成

TERASOLUNA Server Framework for Java (5.x) Development Guideline の 「チュートリアル(Todoアプリケーション REST編)」 と同じようにtodoを公開するためのREST APIを作成します。

API名 HTTP メソッド パス ステータス コード 説明
GET Todos GET /api/v1/todos 200 (OK) Todoリソースを全件取得する。
POST Todos POST /api/v1/todos 201 (Created) Todoリソースを新規作成する。
Get Todo GET /api/v1/todos/{todoId} 200 (OK) Todoリソースを一件取得する。
PUT Todo PUT /api/v1/todos/{todoId} 200 (OK) Todoリソースを完了状態に更新する。
DELETE Todo DELETE /api/v1/todos/{todoId} 204 (No Content) Todoリソースを削除する。

REST APIの実装

以下の手順で実装を進めます。

  • Web API Maker のファクトリクラスの作成
    TodoServiceFactory を作成します。
  • Web API Maker のサービスクラスの作成
    TodoApiService, TodoApiServiceImpl を作成します。
  • packagesファイルの作成
    src/main/resources/META-INF/im_web_api_maker/packages を作成します。

intra-mart Accel Platform のServiceクラスとして、TodoService, TodoServiceImplが既にあるとします。

Web API Maker のファクトリクラスの作成

Web API Maker のサービスクラスに intra-mart Accel Platform の Service クラスをDIするために、 getService() メソッドでは ApplicationContextProvider.getApplicationContext() から Web API Maker のサービスクラスのビーンを取得しています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.sample.todo.api.todo;

import jp.co.intra_mart.foundation.web_api_maker.annotation.ProvideFactory;
import jp.co.intra_mart.foundation.web_api_maker.annotation.ProvideService;
import jp.co.intra_mart.foundation.web_api_maker.annotation.WebAPIMaker;
import jp.co.intra_mart.framework.extension.spring.context.ApplicationContextProvider;

@WebAPIMaker
public class TodoServiceFactory {

    @ProvideFactory
    public static TodoServiceFactory getFactory() {
        return new TodoServiceFactory();
    }

    @ProvideService
    public TodoApiService getService() {
        return ApplicationContextProvider.getApplicationContext().getBean(TodoApiService.class);
    }
}
ファクトリクラスには @WebAPIMaker を記述します。
ファクトリ取得メソッドに @ProvideFactory 、サービスクラス取得メソッドに @ProvideService を記述します。
サービスクラス取得メソッドでは、ApplicationContextProvider.getApplicationContext() から Web API Maker のサービスクラスのビーンを取得します。

Web API Maker のサービスクラスの作成

TodoApiService インタフェースと TodoApiServiceImpl 実装クラスを作成します。 Web API Makerのアノテーションはインタフェース側に記述します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.sample.todo.api.todo;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.intra_mart.foundation.web_api_maker.annotation.BasicAuthentication;
import jp.co.intra_mart.foundation.web_api_maker.annotation.Body;
import jp.co.intra_mart.foundation.web_api_maker.annotation.DELETE;
import jp.co.intra_mart.foundation.web_api_maker.annotation.GET;
import jp.co.intra_mart.foundation.web_api_maker.annotation.POST;
import jp.co.intra_mart.foundation.web_api_maker.annotation.PUT;
import jp.co.intra_mart.foundation.web_api_maker.annotation.Path;
import jp.co.intra_mart.foundation.web_api_maker.annotation.Required;
import jp.co.intra_mart.foundation.web_api_maker.annotation.Response;
import jp.co.intra_mart.foundation.web_api_maker.annotation.Variable;
import com.sample.todo.api.NotFoundException;

@BasicAuthentication(pathPrefix = "")
@Todo
public interface TodoApiService {

    @GET(summary = "Todo情報を取得します。", description = "Todo情報の一覧を取得します。")
    @Path("/api/v1/todos")
    @TodoTag
    public List<TodoResource> getTodos();

    @POST(summary = "Todo情報を登録します。", description = "Todo情報を登録します。idは自動採番します。")
    @Path("/api/v1/todos")
    @Response(code = 201)
    @TodoTag
    public TodoResource postTodos(@Required @Body TodoResource todoResource, HttpServletRequest request, HttpServletResponse response);

    @GET(summary = "Todo情報を取得します。", description = "指定したTodo情報を取得します。指定したTodo情報が無い場合、404を返します。")
    @Path("/api/v1/todos/{todoId}")
    @TodoTag
    public TodoResource getTodo(@Required @Variable(name = "todoId") String todoId) throws NotFoundException;

    @PUT(summary = "Todoを完了します。", description = "Todo情報の完了フラグをONにします。")
    @Path("/api/v1/todos/{todoId}")
    @TodoTag
    public TodoResource putTodo(@Required @Variable(name = "todoId") String todoId);

    @DELETE(summary = "Todo情報を削除します。", description = "Todo情報を削除します。")
    @Path("/api/v1/todos/{todoId}")
    @Response(code = 204)
    @TodoTag
    public void deleteTodo(@Required @Variable(name = "todoId") String todoId);
}
@GET, @POSTなどHTTPメソッドを表しています。 @PathでURIと各メソッドの対応を表しています。
@Responseはステータスコードを指定する時に使います。省略時は200になります。
@BasicAuthentication はベーシック認証を使うことを示すアノテーションです。
@Todo, @TodoTag は APIドキュメントアノテーションです。
それぞれのアノテーションについては、 「Web API Maker プログラミングガイド」を参照してください。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.sample.todo.api.todo;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.github.dozermapper.core.Mapper;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.terasoluna.gfw.common.exception.ResourceNotFoundException;

import com.sample.todo.api.NotFoundException;
import com.sample.todo.domain.model.Todo;
import com.sample.todo.domain.service.todo.TodoService;

@Component
public class TodoApiServiceImpl implements TodoApiService {

    @Inject
    TodoService todoService;

    @Inject
    Mapper mapper;

    @Override
    public List<TodoResource> getTodos() {
        Collection<Todo> todos = todoService.findAll();
        List<TodoResource> todoResources = new ArrayList<>();
        for (Todo todo : todos) {
            todoResources.add(mapper.map(todo, TodoResource.class));
        }
        return todoResources;
    }

    @Override
    public TodoResource postTodos(TodoResource todoResource, HttpServletRequest request, HttpServletResponse response) {
        Todo createdTodo = todoService.create(mapper.map(todoResource, Todo.class));
        TodoResource createdTodoResponse = mapper.map(createdTodo, TodoResource.class);
        ServletUriComponentsBuilder uriBuilder = ServletUriComponentsBuilder.fromRequest(request);
        URI uri = uriBuilder.pathSegment(createdTodoResponse.getTodoId()).build().toUri();
        response.addHeader("Location", uri.toASCIIString());
        return createdTodoResponse;
    }

    @Override
    public TodoResource getTodo(String todoId) throws NotFoundException {
        try {
            Todo todo = todoService.findOne(todoId);
            TodoResource todoResource = mapper.map(todo, TodoResource.class);
            return todoResource;
        } catch (ResourceNotFoundException e) {
            throw new NotFoundException(e.getMessage(), e);
        }
    }

    @Override
    public TodoResource putTodo(String todoId) {
        Todo finishedTodo = todoService.finish(todoId);
        TodoResource finishedTodoResource = mapper.map(finishedTodo, TodoResource.class);
        return finishedTodoResource;
    }

    @Override
    public void deleteTodo(String todoId) {
        todoService.delete(todoId);
    }
}
@Component をつけて TodoApiServiceImplをビーン登録します。
@Inject TodoService で Service をDIします。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.sample.todo.api;

import jp.co.intra_mart.foundation.web_api_maker.annotation.Response;

@Response(code = 404)
public class NotFoundException extends Exception {

    private static final long serialVersionUID = 1L;

    public NotFoundException() {
        super();
    }

    public NotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public NotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public NotFoundException(String message) {
        super(message);
    }

    public NotFoundException(Throwable cause) {
        super(cause);
    }
}
例外時に特定のステータスコードを返したい場合には @Response アノテーションを記述した例外クラスを使います。
ここでは TodoApiServiceImpl#getTodo(String) で NotFoundException がスローされた場合、 404のステータスコードを返します。

packagesファイルの作成

「src/main/resources/META-INF/im_web_api_maker」 ディレクトリに 「packages」 ファイルを作成し、ファクトリクラスのパッケージを記述します。 このサンプルでは、 「com.sample.todo.api.todo」 になります。