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」 になります。